I want to animate a control roll out in my installer.
You can see this video.
You can use a timer to animate a control.
[Code]
function SetTimer(hWnd: longword; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord):
LongWord; external 'SetTimer#user32.dll stdcall';
function KillTimer(hWnd, nIDEvent: LongWord): LongWord;
external 'KillTimer#User32.dll stdcall';
var
MainPanelAnimated: Boolean;
AnimationTimer: LongWord;
procedure AnimationTimerProc(
H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
var
L: Integer;
begin
L := WizardForm.MainPanel.Left + ScaleX(5);
if L > 0 then
begin
L := 0;
KillTimer(0, AnimationTimer);
end;
WizardForm.MainPanel.Left := L;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if WizardForm.OuterNotebook.ActivePage = WizardForm.InnerPage then
begin
if not MainPanelAnimated then
begin
AnimationTimer := SetTimer(0, 0, 5, CreateCallback(#AnimationTimerProc));
WizardForm.MainPanel.Left := -WizardForm.MainPanel.Width;
MainPanelAnimated := True;
end;
end;
end;
For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library.
(the animation is actually more smooth than the image shows)
For right-to-left animation, see Inno Setup - Animate a control roll out from right in a determinate page.
Related
I have prepared simple script that displays image under ProgressGauge bar on wpInstalling Page.
But... I need more complex functionality.
What I need is multiple images show, each after X (e.g. 7) seconds (with loop when installation longer then X secs * number of images) or each after X (e.g. 10) percent of installation. I have tried to embed images display in ProgressGauge.Position, but I failed.
Here is what I have:
procedure CurPageChanged(CurPageID: Integer);
var
BmpFile: TBitmapImage;
begin
ExtractTemporaryFile('01.bmp');
ExtractTemporaryFile('02.bmp');
ExtractTemporaryFile('03.bmp');
if CurPageID = wpInstalling then
begin
BmpFile:= TBitmapImage.Create(WizardForm);
BmpFile.Bitmap.LoadFromFile(ExpandConstant('{tmp}\01.bmp'));
BmpFile.Width:= ScaleX(420);
BmpFile.Height:= ScaleY(180);
BmpFile.Left := WizardForm.ProgressGauge.Left + ScaleX(0);
BmpFile.Top := WizardForm.ProgressGauge.Top + ScaleY(35);
// BmpFile.Parent:= WizardForm.InstallingPage;
// BmpFile:= TBitmapImage.Create(WizardForm);
// BmpFile.Bitmap.LoadFromFile(ExpandConstant('{tmp}\03.bmp'));
// BmpFile.Width:= ScaleX(420);
// BmpFile.Height:= ScaleY(400);
// BmpFile.Left := WizardForm.ProgressGauge.Left + ScaleX(0);
// BmpFile.Top := WizardForm.ProgressGauge.Top + ScaleY(35);
// BmpFile.Parent:= WizardForm.InstallingPage;
// BmpFile:= TBitmapImage.Create(WizardForm);
// BmpFile.Bitmap.LoadFromFile(ExpandConstant('{tmp}\03.bmp'));
// BmpFile.Width:= ScaleX(420);
// BmpFile.Height:= ScaleY(400);
// BmpFile.Left := WizardForm.ProgressGauge.Left + ScaleX(0);
// BmpFile.Top := WizardForm.ProgressGauge.Top + ScaleY(35);
// BmpFile.Parent:= WizardForm.InstallingPage;
end;
end;
The goal is:
On the wpInstalling there should be X images displayed, every next per X seconds or after X percent of installation.
Since the ProgressGauge has no progress change events and there is no way to process setup application messages you will need to use the Windows API timer. This timer needs a callback function which you can't define in Inno Setup script unfortunately so you will need some external library to do this job for you. However there's the InnoCallback library which can do exactly this.
For the following code copy the InnoCallback.dll library into your setup directory, merge this code with your Inno Setup script and implement some kind of a slideshow page turning in the OnSlideTimer event which will be called periodically (with the current settings each second).
[Files]
Source: "InnoCallback.dll"; DestDir: "{tmp}"; Flags: dontcopy
[code]
var
TimerID: Integer;
type
TTimerProc = procedure(Wnd: HWND; Msg: UINT; TimerID: UINT_PTR;
SysTime: DWORD);
function WrapTimerProc(Callback: TTimerProc; ParamCount: Integer): LongWord;
external 'wrapcallback#files:InnoCallback.dll stdcall';
function SetTimer(hWnd: HWND; nIDEvent, uElapse: UINT;
lpTimerFunc: UINT): UINT; external 'SetTimer#user32.dll stdcall';
function KillTimer(hWnd: HWND; uIDEvent: UINT): BOOL;
external 'KillTimer#user32.dll stdcall';
procedure OnSlideTimer(Wnd: HWND; Msg: UINT; TimerID: UINT_PTR;
SysTime: DWORD);
begin
{ here you can turn your slideshow pages; use some variable to store the }
{ current index of the slide you are on, note that this procedure is called }
{ periodically each 1000 ms (see below why), so here you can also check the }
{ progress value, if you want to }
end;
procedure StartSlideTimer;
var
TimerCallback: LongWord;
begin
TimerCallback := WrapTimerProc(#OnSlideTimer, 4);
{ third parameter here is the timer's timeout value in milliseconds }
TimerID := SetTimer(0, 0, 1000, TimerCallback);
end;
procedure KillSlideTimer;
begin
if TimerID <> 0 then
begin
if KillTimer(0, TimerID) then
TimerID := 0;
end;
end;
function InitializeSetup: Boolean;
begin
Result := True;
TimerID := 0;
end;
procedure DeinitializeSetup;
begin
KillSlideTimer;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpInstalling then
StartSlideTimer
else
KillSlideTimer;
end;
I am trying to use this code (with InnoCallback DLL library):
[Code]
var
MainPanelAnimated: Boolean;
AnimationTimer: LongWord;
procedure AnimationTimerProc(
H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
var
L: Integer;
begin
L := WizardForm.MainPanel.Left + ScaleX(5);
if L > 0 then
begin
L := 0;
KillTimer(0, AnimationTimer);
end;
WizardForm.MainPanel.Left := L;
end;
procedure CurPageChanged(CurPageID: Integer);
var
HoverTimerCallback: LongWord;
begin
if WizardForm.OuterNotebook.ActivePage = WizardForm.InnerPage then
begin
if not MainPanelAnimated then
begin
HoverTimerCallback := WrapTimerProc(#AnimationTimerProc, 4);
AnimationTimer := SetTimer(0, 0, 5, HoverTimerCallback);
WizardForm.MainPanel.Left := -WizardForm.MainPanel.Width;
MainPanelAnimated := True;
end;
end;
end;
from How to animate a control roll out in Inno Setup (answer of Martin Prikryl), to show the same effect but from right to left and in a determinate page of setup. How to do this?
Use CurPageID in CurPageChanged to select on what page to show the animation.
[Code]
function SetTimer(hWnd: longword; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord):
LongWord; external 'SetTimer#user32.dll stdcall';
function KillTimer(hWnd, nIDEvent: LongWord): LongWord;
external 'KillTimer#User32.dll stdcall';
var
AnimationTimer: LongWord;
procedure AnimationTimerProc(
H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
var
L: Integer;
begin
L := WizardForm.MainPanel.Left - ScaleX(5);
if L < 0 then
begin
L := 0;
KillTimer(0, AnimationTimer);
end;
WizardForm.MainPanel.Left := L;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpReady then
begin
AnimationTimer := SetTimer(0, 0, 5, CreateCallback(#AnimationTimerProc));
WizardForm.MainPanel.Left := WizardForm.MainPanel.Width;
end;
end;
For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library.
How to close the installer on the "Finished" page after a certain time?
It could also be interpreted as: how to close the installer after some time of non-activity? (close/cancel install). Is this possible?
Setup a timer once the "Finished" page displays to trigger the close.
[Code]
function SetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc: LongWord): LongWord;
external 'SetTimer#User32.dll stdcall';
function KillTimer(hWnd, nIDEvent: LongWord): LongWord;
external 'KillTimer#User32.dll stdcall';
var
PageTimeoutTimer: LongWord;
PageTimeout: Integer;
procedure UpdateFinishButton;
begin
WizardForm.NextButton.Caption :=
Format(SetupMessage(msgButtonFinish) + ' - %ds', [PageTimeout]);
end;
procedure PageTimeoutProc(
H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
begin
if PageTimeout > 1 then
begin
Dec(PageTimeout);
UpdateFinishButton;
end
else
begin
WizardForm.NextButton.OnClick(WizardForm.NextButton);
end;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpFinished then
begin
PageTimeout := 10;
UpdateFinishButton;
PageTimeoutTimer := SetTimer(0, 0, 1000, CreateCallback(#PageTimeoutProc));
end;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
if CurPageID = wpFinished then
begin
KillTimer(0, PageTimeoutTimer);
PageTimeoutTimer := 0;
end;
Result := True;
end;
For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library.
Related questions:
MsgBox - Make unclickable OK Button and change to countdown - Inno Setup;
Inno Setup - Automatically submitting uninstall prompts.
Here's the code...
ifdef UNICODE
#define AW "W"
#else
#define AW "A"
#endif
const
MB_TIMEDOUT = 32000;
MB_ICONERROR = $10;
MB_ICONQUESTION = $20;
MB_ICONWARNING = $30;
MB_ICONINFORMATION = $40;
function MessageBoxTimeout(hWnd: HWND; lpText: string; lpCaption: string;
uType: UINT; wLanguageId: Word; dwMilliseconds: DWORD): Integer;
external 'MessageBoxTimeout{#AW}#user32.dll stdcall';
procedure InitializeWizard;
begin
MessageBoxTimeout(WizardForm.Handle, 'Some ' +
'message', 'Setup', MB_OK or MB_ICONINFORMATION, 0, 5000);
end;
I just want the message box to appear without the button. What code to be added or removed? Where would I insert it? Thanks!
Does this a code from How to disable the “Next” button on the wizard form in Inno Setup? work with my script? I can't seem to to make it working.
You cannot.
But as you already know from MsgBox - Make unclickable OK Button and change to countdown - Inno Setup, you can implement the message box from a scratch yourself. This way, you can customize it any way you want.
Actually, all you need is to remove the button from my answer to the above question.
[Code]
function SetTimer(hWnd: LongWord; nIDEvent, uElapse: LongWord;
lpTimerFunc: LongWord): LongWord; external 'SetTimer#user32.dll stdcall';
function KillTimer(hWnd: HWND; uIDEvent: LongWord): BOOL;
external 'KillTimer#user32.dll stdcall';
var
TimeoutForm: TSetupForm;
procedure TimeoutProc(H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
begin
TimeoutForm.Tag := TimeoutForm.Tag - 1;
if TimeoutForm.Tag = 0 then
begin
TimeoutForm.Close;
end;
end;
procedure TimeoutMessageBoxCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
{ Prevent the dialog from being closed by the X button and Alt-F4 }
CanClose := (TimeoutForm.Tag = 0);
end;
procedure TimeoutMessageBox(Message: string; Seconds: Integer);
var
MessageLabel: TLabel;
Timer: LongWord;
begin
TimeoutForm := CreateCustomForm;
try
TimeoutForm.ClientWidth := ScaleX(256);
TimeoutForm.ClientHeight := ScaleY(64);
TimeoutForm.Caption := 'Information';
TimeoutForm.Position := poMainFormCenter;
TimeoutForm.OnCloseQuery := #TimeoutMessageBoxCloseQuery;
TimeoutForm.Tag := Seconds;
MessageLabel := TLabel.Create(TimeoutForm);
MessageLabel.Top := ScaleY(16);
MessageLabel.Left := ScaleX(16);
MessageLabel.AutoSize := True;
MessageLabel.Caption := Message;
MessageLabel.Parent := TimeoutForm;
Timer := SetTimer(0, 0, 1000, CreateCallback(#TimeoutProc));
try
TimeoutForm.ShowModal();
finally
KillTimer(0, Timer);
end;
finally
TimeoutForm.Free();
TimeoutForm := nil;
end;
end;
For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library.
Is it possible to simulate OnMouseHover event (to call a function when mouse is over some Inno Setup control) for Inno Setup controls, or is there any DLL library which can help?
You can implement it by:
scheduling a very frequent timer (say 50 ms)
when the timer is triggered, find a control over which the cursor is positioned and check for changes.
The following example displays name of the control with cursor over it on a label, like:
[Code]
var
HoverLabel:TLabel;
LastMouse: TPoint;
LastHoverControl: TControl;
function GetCursorPos(var lpPoint: TPoint): BOOL;
external 'GetCursorPos#user32.dll stdcall';
function SetTimer(hWnd: longword; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord):
LongWord; external 'SetTimer#user32.dll stdcall';
function ScreenToClient(hWnd: HWND; var lpPoint: TPoint): BOOL;
external 'ScreenToClient#user32.dll stdcall';
function ClientToScreen(hWnd: HWND; var lpPoint: TPoint): BOOL;
external 'ClientToScreen#user32.dll stdcall';
function FindControl(Parent: TWinControl; P: TPoint): TControl;
var
Control: TControl;
WinControl: TWinControl;
I: Integer;
P2: TPoint;
begin
{ Top-most controls are the last. We want to start with those. }
for I := Parent.ControlCount - 1 downto 0 do
begin
Control := Parent.Controls[I];
if Control.Visible and
(Control.Left <= P.X) and (P.X < Control.Left + Control.Width) and
(Control.Top <= P.Y) and (P.Y < Control.Top + Control.Height) then
begin
if Control is TWinControl then
begin
P2 := P;
ClientToScreen(Parent.Handle, P2);
WinControl := TWinControl(Control);
ScreenToClient(WinControl.Handle, P2);
Result := FindControl(WinControl, P2);
if Result <> nil then Exit;
end;
Result := Control;
Exit;
end;
end;
Result := nil;
end;
procedure HoverControlChanged(Control: TControl);
begin
if Control = nil then
begin
HoverLabel.Caption := 'no control';
end
else
begin
HoverLabel.Caption := Control.Name;
end;
end;
procedure HoverTimerProc(
H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
var
P: TPoint;
Control: TControl;
begin
GetCursorPos(P);
if P <> LastMouse then { just optimization }
begin
LastMouse := P;
ScreenToClient(WizardForm.Handle, P);
if (P.X < 0) or (P.Y < 0) or
(P.X > WizardForm.ClientWidth) or (P.Y > WizardForm.ClientHeight) then
begin
Control := nil;
end
else
begin
Control := FindControl(WizardForm, P);
end;
if Control <> LastHoverControl then
begin
HoverControlChanged(Control);
LastHoverControl := Control;
end;
end;
end;
procedure InitializeWizard();
begin
SetTimer(0, 0, 50, CreateCallback(#HoverTimerProc));
HoverLabel := TLabel.Create(WizardForm);
HoverLabel.Left := ScaleX(8);
HoverLabel.Top := WizardForm.ClientHeight - ScaleY(32);
HoverLabel.Parent := WizardForm;
HoverLabel.Caption := 'starting';
end;
For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library.
An alternative way to implement this without a timer is to handle relevant windows messages in a handler set using GWL_WNDPROC. For an example how to set the handler, see WM_CONTEXTMENU handling in Adding context menu to Inno Setup page.
The following code is from the documentation of Inno Unicode Enhanced Ver. As you can see the OnMouseEnter & OnMouseLeave functions, you can use them to implement your OnHover function.
TButton = class(TButtonControl)
procedure Click;
property OnMouseEnter: TNotifyEvent; read write;
property OnMouseLeave: TNotifyEvent; read write;
end;