| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822 |
- unit Setup.SetupForm;
- {
- Inno Setup
- Copyright (C) 1997-2025 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- TSetupForm
- Also used by UninstallProgressForm and UninstallSharedFileForm!
- Requires following globals to be set:
- -IsDarkInstallMode
- -LangOptions.RightToLeft
- -LangOptions.DialogFontName
- -LangOptions.DialogFontSize
- -LangOptions.DialogFontBaseScaleWidth
- -LangOptions.DialogFontBaseScaleHeight
- -SetupHeader.WizardLightControlStyling
- -shWizardKeepAspectRatio in SetupHeader.Options
- Also requires following globals to be set, but 0 is allowed:
- -SetupHeader.WizardSizePercentX
- -SetupHeader.WizardSizePercentY
- }
- interface
- uses
- Windows, SysUtils, Messages, Classes, Graphics, Controls, Forms, Dialogs,
- UIStateForm,
- Setup.MainFunc;
- type
- TSetupForm = class(TUIStateForm)
- private
- FOrigBaseUnitX, FOrigBaseUnitY: Integer;
- FBaseUnitX, FBaseUnitY: Integer;
- FRightToLeft: Boolean;
- FFlipControlsOnShow: Boolean;
- FCenterOnShow: Boolean;
- FControlsFlipped: Boolean;
- FKeepSizeX, FKeepSizeY: Boolean;
- FOrigClientWidthAfterScale, FOrigClientHeightAfterScale: Integer;
- FSetForeground: Boolean;
- FDidDisableChildControlsStylesAsNeeded: Boolean;
- class function ShouldDisableControlStylesAsNeeded: Boolean;
- class procedure DisableControlStyleAsNeeded(const Ctl: TControl);
- procedure CMShowingChanged(var Message: TMessage); message CM_SHOWINGCHANGED;
- procedure WMQueryEndSession(var Message: TWMQueryEndSession); message WM_QUERYENDSESSION;
- protected
- procedure Center;
- procedure CenterInsideControl(const Ctl: TWinControl;
- const InsideClientArea: Boolean);
- procedure CenterInsideRect(const InsideRect: TRect);
- procedure CreateParams(var Params: TCreateParams); override;
- procedure CreateWnd; override;
- function GetExtraClientWidth: Integer;
- function GetExtraClientHeight: Integer;
- procedure FlipControlsIfNeeded;
- procedure CenterIfNeeded(const ACenterInsideControl: Boolean;
- const CenterInsideControlCtl: TWinControl;
- const CenterInsideControlInsideClientArea: Boolean);
- procedure VisibleChanging; override;
- procedure WndProc(var Message: TMessage); override;
- public
- constructor Create(AOwner: TComponent); override;
- constructor CreateNew(AOwner: TComponent; Dummy: Integer = 0); override;
- function CalculateButtonWidth(const ButtonCaptions: array of String): Integer;
- procedure InitializeFont(const KeepSizeX: Boolean = False; const KeepSizeY: Boolean = False);
- class function ScalePixelsX(const OrigBaseUnitX, BaseUnitX, N: Integer): Integer; overload;
- class function ScalePixelsY(const OrigBaseUnitY, BaseUnitY, N: Integer): Integer; overload;
- class procedure SetCtlParent(const AControl: TControl; const AParent: TWinControl);
- function ScalePixelsX(const N: Integer): Integer; overload;
- function ScalePixelsY(const N: Integer): Integer; overload;
- procedure SetBackImage(const BackImages: TWizardImages; const Stretch, Center: Boolean; const Opacity: Byte; const Redraw: Boolean); overload;
- function ShouldSizeX: Boolean;
- function ShouldSizeY: Boolean;
- function ShowModal: Integer; override;
- procedure FlipAndCenterIfNeeded(const ACenterInsideControl: Boolean = False;
- const CenterInsideControlCtl: TWinControl = nil;
- const CenterInsideControlInsideClientArea: Boolean = False); virtual;
- property BaseUnitX: Integer read FBaseUnitX;
- published
- property CenterOnShow: Boolean read FCenterOnShow write FCenterOnShow;
- property ControlsFlipped: Boolean read FControlsFlipped;
- property ExtraClientWidth: Integer read GetExtraClientWidth;
- property ExtraClientHeight: Integer read GetExtraClientHeight;
- property FlipControlsOnShow: Boolean read FFlipControlsOnShow write FFlipControlsOnShow;
- property KeepSizeX: Boolean read FKeepSizeX;
- property KeepSizeY: Boolean read FKeepSizeY;
- property RightToLeft: Boolean read FRightToLeft;
- property SetForeground: Boolean read FSetForeground write FSetForeground;
- end;
- procedure CalculateBaseUnitsFromFont(const Font: TFont; var X, Y: Integer);
- function SetFontNameSize(const AFont: TFont; const AName: String;
- const ASize: Integer; const AFallbackName: String;
- const AFallbackSize: Integer): Boolean;
- implementation
- uses
- Generics.Collections, UITypes, WinXPanels, Themes, StdCtrls, ExtCtrls,
- BidiUtils, BitmapButton, BitmapImage, NewNotebook, NewStaticText, NewCheckListBox, FormBackgroundStyleHook,
- Shared.Struct, Shared.CommonFunc, Shared.CommonFunc.Vcl,
- Setup.InstFunc;
- var
- WM_QueryCancelAutoPlay: UINT;
- function GetRectOfPrimaryMonitor(const WorkArea: Boolean): TRect;
- begin
- if not WorkArea or
- not SystemParametersInfo(SPI_GETWORKAREA, 0, @Result, 0) then
- Result := Rect(0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
- end;
- function SetFontNameSize(const AFont: TFont; const AName: String;
- const ASize: Integer; const AFallbackName: String;
- const AFallbackSize: Integer): Boolean;
- { Returns True if AName <> '' and it used AName as the font name,
- False otherwise. }
- function SizeToHeight(const S: Integer): Integer;
- begin
- Result := MulDiv(-S, Screen.PixelsPerInch, 72);
- end;
- begin
- Result := False;
- if AName <> '' then begin
- if FontExists(AName) then begin
- AFont.Name := AName;
- AFont.Height := SizeToHeight(ASize);
- Result := True;
- Exit;
- end;
- { Note: AFallbackName is not used if the user specified an empty string for
- AName because in that case they want the default font used always }
- if (AFallbackName <> '') and FontExists(AFallbackName) then begin
- AFont.Name := AFallbackName;
- AFont.Height := SizeToHeight(AFallbackSize);
- Exit;
- end;
- end;
- AFont.Name := 'Segoe UI';
- AFont.Height := SizeToHeight(AFallbackSize);
- end;
- procedure CalculateBaseUnitsFromFont(const Font: TFont; var X, Y: Integer);
- var
- DC: HDC;
- Size: TSize;
- TM: TTextMetric;
- begin
- DC := GetDC(0);
- try
- SelectObject(DC, Font.Handle);
- { Based on code from Q145994: }
- GetTextExtentPoint(DC,
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 52, Size);
- X := (Size.cx div 26 + 1) div 2;
- GetTextMetrics(DC, TM);
- Y := TM.tmHeight;
- finally
- ReleaseDC(0, DC);
- end;
- end;
- function IsParentSetupFormFlipped(AControl: TControl): Boolean;
- function GetParentSetupForm(AControl: TControl): TSetupForm;
- begin
- { Note: Unlike GetParentForm, this checks all levels, not just the top }
- repeat
- if AControl is TSetupForm then begin
- Result := TSetupForm(AControl);
- Exit;
- end;
- AControl := AControl.Parent;
- until AControl = nil;
- Result := nil;
- end;
- var
- ParentForm: TSetupForm;
- begin
- ParentForm := GetParentSetupForm(AControl);
- if Assigned(ParentForm) then
- Result := ParentForm.ControlsFlipped
- else
- Result := False;
- end;
- function GetPPI(const Wnd: HWND): Integer;
- begin
- { Based on TSysStyleHook.GetCurrentPPI }
- if CheckPerMonitorV2SupportForWindow(Wnd) then begin { Currently always False in Setup }
- { GetDPIForWindow requires Windows 10 version 1607. However, because it is delay-loaded and it's
- never executed on older versions of Windows, it does not cause entry point not found errors. }
- Result := Integer(GetDPIForWindow(Wnd))
- end else
- Result := Screen.PixelsPerInch;
- end;
- { TSetupForm }
- constructor TSetupForm.Create(AOwner: TComponent);
- begin
- { Must initialize FRightToLeft here in addition to CreateNew because
- CreateNew isn't virtual on Delphi 2 and 3 }
- FRightToLeft := LangOptions.RightToLeft;
- FFlipControlsOnShow := FRightToLeft;
- FCenterOnShow := True;
- inherited;
- { Setting BidiMode before inherited causes an AV when TControl tries to
- send CM_BIDIMODECHANGED. This is why we have additonal RTL code in
- CreateParams below. }
- if FRightToLeft then
- BiDiMode := bdRightToLeft;
- { In Delphi 2005 and later, Position defaults to poDefaultPosOnly, but we
- don't want the form to be changing positions whenever its handle is
- recreated, so change it to the D7 and earlier default of poDesigned. }
- if Position = poDefaultPosOnly then
- Position := poDesigned;
- end;
- constructor TSetupForm.CreateNew(AOwner: TComponent; Dummy: Integer = 0);
- begin
- { Note: On Delphi 2 and 3, CreateNew isn't virtual, so this is only reached
- when TSetupForm.CreateNew is called explicitly }
- FRightToLeft := LangOptions.RightToLeft;
- FFlipControlsOnShow := FRightToLeft;
- FCenterOnShow := True;
- inherited;
- if FRightToLeft then
- BiDiMode := bdRightToLeft;
- end;
- function TSetupForm.CalculateButtonWidth(const ButtonCaptions: array of String): Integer;
- var
- DC: HDC;
- W: Integer;
- begin
- Result := ScalePixelsX(75);
- { Increase the button size if there are unusually long button captions }
- DC := GetDC(0);
- try
- SelectObject(DC, Font.Handle);
- for var I := Low(ButtonCaptions) to High(ButtonCaptions) do begin
- W := GetTextWidth(DC, ButtonCaptions[I], True) + ScalePixelsX(20);
- if Result < W then
- Result := W;
- end;
- finally
- ReleaseDC(0, DC);
- end;
- end;
- procedure TSetupForm.CenterInsideControl(const Ctl: TWinControl;
- const InsideClientArea: Boolean);
- var
- R: TRect;
- begin
- const CtlForm = GetParentForm(Ctl);
- if (CtlForm = nil) or not IsWindowVisible(CtlForm.Handle) or
- IsIconic(CtlForm.Handle) then begin
- Center;
- Exit;
- end;
- if not InsideClientArea then begin
- if GetWindowRect(Ctl.Handle, R) then
- CenterInsideRect(R);
- end
- else begin
- R := Ctl.ClientRect;
- MapWindowPoints(Ctl.Handle, 0, R, 2);
- CenterInsideRect(R);
- end;
- end;
- procedure TSetupForm.CenterInsideRect(const InsideRect: TRect);
- function GetRectOfMonitorContainingRect(const R: TRect): TRect;
- { Returns bounding rectangle of monitor containing or nearest to R }
- type
- HMONITOR = type THandle;
- TMonitorInfo = record
- cbSize: DWORD;
- rcMonitor: TRect;
- rcWork: TRect;
- dwFlags: DWORD;
- end;
- const
- MONITOR_DEFAULTTONEAREST = $00000002;
- var
- Module: HMODULE;
- MonitorFromRect: function(const lprc: TRect; dwFlags: DWORD): HMONITOR; stdcall;
- GetMonitorInfo: function(hMonitor: HMONITOR; var lpmi: TMonitorInfo): BOOL; stdcall;
- M: HMONITOR;
- Info: TMonitorInfo;
- begin
- Module := GetModuleHandle(user32);
- MonitorFromRect := GetProcAddress(Module, 'MonitorFromRect');
- GetMonitorInfo := GetProcAddress(Module, 'GetMonitorInfoA');
- if Assigned(MonitorFromRect) and Assigned(GetMonitorInfo) then begin
- M := MonitorFromRect(R, MONITOR_DEFAULTTONEAREST);
- Info.cbSize := SizeOf(Info);
- if GetMonitorInfo(M, Info) then begin
- Result := Info.rcWork;
- Exit;
- end;
- end;
- Result := GetRectOfPrimaryMonitor(True);
- end;
- var
- R, MR: TRect;
- begin
- R := Bounds(InsideRect.Left + ((InsideRect.Right - InsideRect.Left) - Width) div 2,
- InsideRect.Top + ((InsideRect.Bottom - InsideRect.Top) - Height) div 2,
- Width, Height);
- { Clip to nearest monitor }
- MR := GetRectOfMonitorContainingRect(R);
- if R.Right > MR.Right then
- OffsetRect(R, MR.Right - R.Right, 0);
- if R.Bottom > MR.Bottom then
- OffsetRect(R, 0, MR.Bottom - R.Bottom);
- if R.Left < MR.Left then
- OffsetRect(R, MR.Left - R.Left, 0);
- if R.Top < MR.Top then
- OffsetRect(R, 0, MR.Top - R.Top);
- BoundsRect := R;
- end;
- procedure TSetupForm.Center;
- begin
- CenterInsideRect(GetRectOfPrimaryMonitor(True));
- end;
- procedure TSetupForm.CreateParams(var Params: TCreateParams);
- begin
- inherited;
- { With Application.MainFormOnTaskBar=True, by default, a form won't get a
- taskbar button if the main form hasn't been created yet (due to the owner
- being an invisible Application.Handle), or if the main form exists but
- isn't visible (e.g., because it's a silent install). Force it to have a
- taskbar button in those cases by specifying no owner for the window.
- (Another method is to set WS_EX_APPWINDOW and leave WndParent set to
- Application.Handle, but it doesn't quite work correctly: if the form
- displays a message box, and you activate another app's window, clicking on
- the form's taskbar button activates the message box again, but the taskbar
- button doesn't change to a "selected" state.) }
- if (Params.WndParent <> 0) and
- (Application.MainFormOnTaskBar or (Params.WndParent <> Application.Handle)) and
- not IsWindowOnTaskbar(Params.WndParent) then
- Params.WndParent := 0;
- { See comment in Create. Also: The following does not make the title bar RTL.
- Achieving this requires adding WS_EX_LAYOUTRTL, which VCL does not support. }
- if FRightToLeft then
- Params.ExStyle := Params.ExStyle or (WS_EX_RTLREADING or WS_EX_LEFTSCROLLBAR or WS_EX_RIGHT);
- end;
- class function TSetupForm.ShouldDisableControlStylesAsNeeded: Boolean;
- begin
- Result := not IsDarkInstallMode and (SetupHeader.WizardLightControlStyling <> wcsAll);
- if Result then begin
- const LStyle = StyleServices;
- Result := LStyle.Enabled and not LStyle.IsSystemStyle;
- end;
- end;
- class procedure TSetupForm.DisableControlStyleAsNeeded(const Ctl: TControl);
- { Call ShouldDisableControlStylesAsNeeded first }
- begin
- { SetupHeader.WizardLightControlStyling is either wcsAllButButtons or wcsOnlyRequired,
- so for buttons the style must always be disabled. }
- if Ctl is TCustomButton then
- Ctl.StyleName := TStyleManager.SystemStyleName
- else if SetupHeader.WizardLightControlStyling = wcsOnlyRequired then begin
- if (Ctl is TNewCheckListBox) and TNewCheckListBox(Ctl).TransparentIfStyled then begin
- { Requires VCL Styles for transparency, but can be told to use native checkboxes and radiobuttons }
- TNewCheckListBox(Ctl).DisableStyledButtons := True;
- end else begin
- const KeepStyle =
- (Ctl is TCustomPanel) or
- (Ctl is TBitmapButton) or (Ctl is TBitmapImage) or { Don't use VCL Styles }
- (Ctl is TNewNotebook) or (Ctl is TNewNotebookPage) or { Don't use VCL Styles }
- ((Ctl is TNewStaticText) and TNewStaticText(Ctl).Transparent); { Requires VCL Styles for transparency }
- if not KeepStyle then
- Ctl.StyleName := TStyleManager.SystemStyleName;
- end;
- end;
- end;
- procedure TSetupForm.CreateWnd;
- procedure DisableChildControlsStylesAsNeeded(const ParentCtl: TWinControl);
- { Call ShouldDisableControlStylesAsNeeded first }
- begin
- for var I := 0 to ParentCtl.ControlCount-1 do begin
- const Ctl = ParentCtl.Controls[I];
- if Ctl is TWinControl then begin
- const WinCtl = Ctl as TWinControl;
- { Sanity check that the control's handle isn't already allocated,
- because otherwise it would run TWinControl.UpdateStyleElements
- which does a RecreateWnd. Might work but isn't efficient. }
- if WinCtl.HandleAllocated then
- InternalError('Unexpected HandleAllocated');
- { Update children }
- DisableChildControlsStylesAsNeeded(WinCtl);
- end;
- { Update self }
- DisableControlStyleAsNeeded(Ctl);
- end;
- end;
- procedure SetControlsCurrentPPI(const Ctl: TWinControl; const PPI: Integer);
- begin
- for var I := 0 to Ctl.ControlCount-1 do begin
- const C = Ctl.Controls[I];
- if C is TWinControl then begin
- SetControlsCurrentPPI(TWinControl(C), PPI);
- C.SetCurrentPPI(PPI);
- end else
- C.SetCurrentPPI(PPI)
- end;
- end;
- begin
- { DisableChildControlsStylesAsNeeded works both before and after
- calling inherited. But it does require the child controls to have
- no handle allocated, which is why it's in CreateWnd and not in
- Create: in Create it can't be before inherited since it wouldn't
- yet know about the children, and also not after since the
- handles might be allocated. }
- if ShouldDisableControlStylesAsNeeded and not FDidDisableChildControlsStylesAsNeeded then begin
- DisableChildControlsStylesAsNeeded(Self);
- { Don't need to disable again if the window is recreated }
- FDidDisableChildControlsStylesAsNeeded := True;
- end;
- inherited;
- if WM_QueryCancelAutoPlay <> 0 then
- AddToWindowMessageFilterEx(Handle, WM_QueryCancelAutoPlay);
- { SetDarkTitleBar requires Handle to be allocated, which is why we
- we call it after calling inherited. }
- if (TStyleManager.FormBorderStyle = fbsSystemStyle) or not (seBorder in StyleElements) then
- SetDarkTitleBar(Self, IsDarkInstallMode);
- { We don't use the Scaled property for scaling and this means the CurrentPPI property will not be
- set correctly. This causes problems when VCL code inspects it, for example in THintWindow.CalcHintRect
- and FormStyleHook.GetBorderSize. So we should update it ourselves by directly writing to the
- FCurrentPPI private variable of the form and all controls on it, which we can do using a class
- helper. Note: Doing it later for the form causes issues with incorrect non-client vs. client size
- when styled title bars are enabled. }
- const PPI = GetPPI(Handle);
- SetCurrentPPI(PPI);
- SetControlsCurrentPPI(Self, PPI);
- { Now that CurrentPPI of the form is set you must make sure that any controls you later parent to
- the form already have the same CurrentPPI, otherwise VCL will scale the controls. Currently this
- is done in:
- -Setup.ScriptClasses's TControlParentW and TNewNotebookPageNotebook_W
- -Setup.WizardForm.CustomPages's SetCtlParent
- -TWizardForm.AddPage
- To debug/detect scaling add a breakpoint in Vcl.Controls' TWinControl.ChangeScale and set project
- option Building->Delphi Compiler->Compiling->Debugging->Use debug .dcus. }
- end;
- procedure TSetupForm.FlipControlsIfNeeded;
- begin
- if FFlipControlsOnShow then begin
- FFlipControlsOnShow := False;
- FControlsFlipped := not FControlsFlipped;
- FlipControls(Self);
- end;
- end;
- procedure TSetupForm.CenterIfNeeded(const ACenterInsideControl: Boolean; const CenterInsideControlCtl: TWinControl; const CenterInsideControlInsideClientArea: Boolean);
- begin
- if FCenterOnShow then begin
- FCenterOnShow := False;
- { Center }
- if ACenterInsideControl and (CenterInsideControlCtl <> nil) then
- CenterInsideControl(CenterInsideControlCtl, CenterInsideControlInsideClientArea)
- else
- Center;
- end;
- end;
- function TSetupForm.ShouldSizeX: Boolean;
- begin
- Result := not FKeepSizeX and (SetupHeader.WizardSizePercentX > 100);
- end;
- function TSetupForm.ShouldSizeY: Boolean;
- begin
- Result := not FKeepSizeY and (SetupHeader.WizardSizePercentY > 100);
- end;
- procedure TSetupForm.FlipAndCenterIfNeeded(const ACenterInsideControl: Boolean;
- const CenterInsideControlCtl: TWinControl; const CenterInsideControlInsideClientArea: Boolean);
- begin
- FlipControlsIfNeeded;
- CenterIfNeeded(ACenterInsideControl, CenterInsideControlCtl, CenterInsideControlInsideClientArea);
- end;
- type
- TControlAccess = class(TControl);
- procedure TSetupForm.InitializeFont(const KeepSizeX, KeepSizeY: Boolean);
- procedure NewChangeScale(const Ctl: TControl; const XM, XD, YM, YD: Integer);
- var
- X, Y, W, H: Integer;
- begin
- X := MulDiv(Ctl.Left, XM, XD);
- Y := MulDiv(Ctl.Top, YM, YD);
- if not(csFixedWidth in Ctl.ControlStyle) then
- W := MulDiv(Ctl.Width, XM, XD)
- else
- W := Ctl.Width;
- if not(csFixedHeight in Ctl.ControlStyle) then
- H := MulDiv(Ctl.Height, YM, YD)
- else
- H := Ctl.Height;
- Ctl.SetBounds(X, Y, W, H);
- end;
- procedure NewScaleControls(const Ctl: TWinControl; const XM, XD, YM, YD: Integer);
- { This is like TControl.ScaleControls, except it allows the width and height
- to be scaled independently }
- var
- I: Integer;
- C: TControl;
- begin
- for I := 0 to Ctl.ControlCount-1 do begin
- C := Ctl.Controls[I];
- if C is TWinControl then begin
- TWinControl(C).DisableAlign;
- try
- NewScaleControls(TWinControl(C), XM, XD, YM, YD);
- NewChangeScale(C, XM, XD, YM, YD);
- finally
- TWinControl(C).EnableAlign;
- end;
- end
- else
- NewChangeScale(C, XM, XD, YM, YD);
- end;
- end;
- type
- TControlAnchorsList = TDictionary<TControl, TAnchors>;
- procedure StripAndStoreChildControlCustomAnchors(const ParentCtl: TControl; const AnchorsList: TControlAnchorsList);
- begin
- if ParentCtl is TWinControl then begin
- const ParentWinCtl = TWinControl(ParentCtl);
- for var I := 0 to ParentWinCtl.ControlCount-1 do begin
- const Ctl = ParentWinCtl.Controls[I];
- if Ctl.Anchors <> [akLeft, akTop] then begin
- AnchorsList.Add(Ctl, Ctl.Anchors);
- { Before we can set Anchors to [akLeft, akTop] (which has a special
- 'no anchors' meaning to VCL), we first need to update the Explicit*
- properties so the control doesn't get moved back to an old position }
- TControlAccess(Ctl).UpdateExplicitBounds;
- Ctl.Anchors := [akLeft, akTop];
- end;
- StripAndStoreChildControlCustomAnchors(Ctl, AnchorsList);
- end;
- end;
- end;
- procedure RestoreAnchors(const AnchorsList: TControlAnchorsList);
- begin
- { The order in which we restore the anchors shouldn't matter, so just
- enumerate the list }
- for var Item in AnchorsList do
- Item.Key.Anchors := Item.Value;
- end;
- function ExcludeFromParentHandlesNeeded(const ParentCtl: TWinControl): Boolean;
- begin
- { Right-aligned TStackPanels are excluded. For example, calling
- HandleNeeded on TaskDialogForm's BottomStackPanel causes it to become
- left-aligned instead of right-aligned. This occurs regardless of the
- timing of the HandleNeeded call, such as before or after sizing. }
- Result := (ParentCtl is TStackPanel) and (TStackPanel(ParentCtl).Align = alRight);
- end;
- procedure ParentHandlesNeeded(const ParentCtl: TControl);
- begin
- if ParentCtl is TWinControl then begin
- const ParentWinCtl = TWinControl(ParentCtl);
- if (ParentWinCtl.ControlCount > 0) and not ExcludeFromParentHandlesNeeded(ParentWinCtl) then begin
- if not (ParentWinCtl is TNewNotebook) then { For notebooks: only need handles on pages }
- ParentWinCtl.HandleNeeded;
- for var I := 0 to ParentWinCtl.ControlCount-1 do
- ParentHandlesNeeded(ParentWinCtl.Controls[I]);
- end;
- end;
- end;
- begin
- { Create parent handles.
- Various things related to positioning and anchoring don't work without this:
- you get positions of child controls back as if there was no anchoring until
- handles are automatically created.
- Initially we did this only when sizing the form (for WizardForm it worked if
- done after sizing but for UninstallProgressForm it had be done before sizing,
- for unknown reasons).
- For WizardForm's BeveledLabel though, it needs it before the font name/size
- change (again for unknown reasons), otherwise the label will end up in the
- wrong position, even if all we do is changing the font. Setting AutoSize to
- False also causes it to stay in the correct position. (To see the bad
- positioning for WizardForm.BeveledLabel you would first have to disable next
- ParentHandlesNeeded call and then also the automatic vertical recentering in
- WizardForm.)
- Doing it always, instead of only before or after sizing, also helps
- TaskDialogForm which does its own sizing (so KeepSizeX and KeepSizeY are
- both True), but still needs parent handles to be created to avoid the issue.
- Note: Caller should make sure the created handles do not get lost again. For
- example, setting StyleElements to [] on a parent would cause the handle to
- get deallocated again, and would reintroduce the issue. So this must be done
- before calling us, and not after. }
- ParentHandlesNeeded(Self); { Also see ShowModal }
- { Set font. Note: Must keep the following lines in synch with Setup.ScriptFunc.pas's
- InitializeScaleBaseUnits }
- SetFontNameSize(Font, LangOptions.DialogFontName, LangOptions.DialogFontSize, '', 9);
- CalculateBaseUnitsFromFont(Font, FBaseUnitX, FBaseUnitY);
- FOrigBaseUnitX := LangOptions.DialogFontBaseScaleWidth;
- FOrigBaseUnitY := LangOptions.DialogFontBaseScaleHeight;
- if shWizardKeepAspectRatio in SetupHeader.Options then begin
- if FBaseUnitX * FOrigBaseUnitY > FBaseUnitY * FOrigBaseUnitX then begin
- FBaseUnitY := FBaseUnitX;
- FOrigBaseUnitY := FOrigBaseUnitX;
- end else begin
- FBaseUnitX := FBaseUnitY;
- FOrigBaseUnitX := FOrigBaseUnitY;
- end;
- end;
- { Scale }
- if (FBaseUnitX <> FOrigBaseUnitX) or (FBaseUnitY <> FOrigBaseUnitY) then begin
- const ControlAnchorsList = TControlAnchorsList.Create;
- try
- { Custom anchors interfere with our scaling code, so strip them and restore
- afterward }
- StripAndStoreChildControlCustomAnchors(Self, ControlAnchorsList);
- { Loosely based on scaling code from TForm.ReadState: }
- NewScaleControls(Self, FBaseUnitX, FOrigBaseUnitX, FBaseUnitY, FOrigBaseUnitY);
- const R = ClientRect;
- const W = MulDiv(R.Right, FBaseUnitX, FOrigBaseUnitX);
- const H = MulDiv(R.Bottom, FBaseUnitY, FOrigBaseUnitY);
- SetBounds(Left, Top, W + (Width - R.Right), H + (Height - R.Bottom));
- finally
- RestoreAnchors(ControlAnchorsList);
- ControlAnchorsList.Free;
- end;
- end;
- { Size }
- FKeepSizeX := KeepSizeX;
- FKeepSizeY := KeepSizeY;
- FOrigClientWidthAfterScale := ClientWidth;
- FOrigClientHeightAfterScale := ClientHeight;
- const LShouldSizeX = ShouldSizeX;
- const LShouldSizeY = ShouldSizeY;
- if LShouldSizeX then
- ClientWidth := MulDiv(ClientWidth, SetupHeader.WizardSizePercentX, 100);
- if LShouldSizeY then
- ClientHeight := MulDiv(ClientHeight, SetupHeader.WizardSizePercentY, 100);
- end;
- function TSetupForm.GetExtraClientWidth: Integer;
- begin
- Result := ClientWidth - FOrigClientWidthAfterScale;
- end;
- function TSetupForm.GetExtraClientHeight: Integer;
- begin
- Result := ClientHeight - FOrigClientHeightAfterScale;
- end;
- class function TSetupForm.ScalePixelsX(const OrigBaseUnitX, BaseUnitX, N: Integer): Integer;
- begin
- Result := MulDiv(N, BaseUnitX, OrigBaseUnitX);
- end;
- function TSetupForm.ScalePixelsX(const N: Integer): Integer;
- begin
- Result := ScalePixelsX(FOrigBaseUnitX, FBaseUnitX, N);
- end;
- class function TSetupForm.ScalePixelsY(const OrigBaseUnitY, BaseUnitY, N: Integer): Integer;
- begin
- Result := MulDiv(N, BaseUnitY, OrigBaseUnitY);
- end;
- function TSetupForm.ScalePixelsY(const N: Integer): Integer;
- begin
- Result := ScalePixelsY(FOrigBaseUnitY, FBaseUnitY, N);
- end;
- procedure TSetupForm.SetBackImage(const BackImages: TWizardImages; const Stretch, Center: Boolean;
- const Opacity: Byte; const Redraw: Boolean);
- begin
- if not CustomWizardBackground then
- InternalError('Cannot set a background image at this time: custom wizard background not active');
- const Graphic = SelectBestImage(BackImages, ClientWidth, ClientHeight);
- TFormBackgroundStyleHook.Graphic := Graphic;
- TFormBackgroundStyleHook.GraphicTarget := Self;
- TFormBackgroundStyleHook.Stretch := Stretch;
- TFormBackgroundStyleHook.Center := Center;
- TFormBackgroundStyleHook.Opacity := Opacity;
- TNewCheckListBox.ComplexParentBackground := Graphic <> nil;
- if Redraw and HandleAllocated then
- RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_ERASE or RDW_UPDATENOW or RDW_ALLCHILDREN);
- end;
- class procedure TSetupForm.SetCtlParent(const AControl: TControl; const AParent: TWinControl);
- { To be called when a control is added after the form has already been created }
- begin
- { Disable style if needed }
- if ShouldDisableControlStylesAsNeeded then
- DisableControlStyleAsNeeded(AControl);
- { Set CurrentPPI of the control to be parented to the CurrentPPI of the parent, preventing VCL
- from scaling the control. Also see TSetupForm.CreateWnd. }
- AControl.SetCurrentPPI(AParent.CurrentPPI);
- AControl.Parent := AParent;
- end;
- function TSetupForm.ShowModal: Integer;
- begin
- { Work around VCL issue (Delphi 11.3): ShowModal calls DisableTaskWindows
- without ensuring the form's handle has been created first. If the handle
- is created after DisableTaskWindows, PopupMode=pmAuto breaks;
- TCustomForm.CreateParams finds that the active window is disabled, and
- doesn't use it as the owner. It then falls back to pmNone behavior, which
- is to use the main form or application window as the owner. }
- HandleNeeded; { Also see InitializeFont }
- Result := inherited;
- end;
- procedure TSetupForm.VisibleChanging;
- begin
- inherited;
- { Note: Unlike DoShow, any exceptions raised in VisibleChanging will be
- propagated out, which is what we want }
- if not Visible then
- FlipAndCenterIfNeeded;
- end;
- procedure TSetupForm.CMShowingChanged(var Message: TMessage);
- begin
- inherited;
- { This usually just makes the taskbar button flash }
- if FSetForeground and Showing then
- SetForegroundWindow(Handle);
- end;
- procedure TSetupForm.WMQueryEndSession(var Message: TWMQueryEndSession);
- begin
- { TDummyClass.AntiShutdownHook in Setup.dpr already denies shutdown attempts
- but we also need to catch WM_QUERYENDSESSION here to suppress the VCL's
- default handling which calls CloseQuery. We do not want to let TMainForm &
- TNewDiskForm display any 'Exit Setup?' message boxes since we're already
- denying shutdown attempts, and also we can't allow them to potentially be
- displayed on top of another dialog box that's already displayed. }
- { Return zero, except if RestartInitiatedByThisProcess is set (which means
- we called RestartComputer previously) }
- if RestartInitiatedByThisProcess then
- Message.Result := 1;
- end;
- procedure TSetupForm.WndProc(var Message: TMessage);
- begin
- { When we receive a 'QueryCancelAutoPlay' message as a result of a new CD
- being inserted, return 1 to prevent it from being 'autoplayed'.
- Note: According to the docs, this message is only sent on Shell version
- 4.70 and later. }
- if (WM_QueryCancelAutoPlay <> 0) and (Message.Msg = WM_QueryCancelAutoPlay) then
- Message.Result := 1
- else
- inherited;
- end;
- initialization
- BidiUtils.IsParentFlippedFunc := IsParentSetupFormFlipped;
- WM_QueryCancelAutoPlay := RegisterWindowMessage('QueryCancelAutoPlay');
- end.
|