inno-setuppascalscript

Inno Setup OnHover event


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?


Solution

  • You can implement it by:

    The following example displays name of the control with cursor over it on a label, like:

    enter image description here

    [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.