Browse Source

Merge branch 'newbitbtn'

Martijn Laan 1 month ago
parent
commit
764d214328

+ 193 - 0
Components/BitmapButton.pas

@@ -0,0 +1,193 @@
+unit BitmapButton;
+
+{
+  Inno Setup
+  Copyright (C) 1997-2025 Jordan Russell
+  Portions by Martijn Laan
+  For conditions of distribution and use, see LICENSE.TXT.
+
+  A TImage-like component for bitmaps without the TPicture bloat and
+  which is actually a button with a focus rectangle when focused - in
+  other words: an accessible TImage
+  
+  Make sure to set the Caption property, even if it isn't visible
+
+  Also see TBitmapImage which is the TGraphicControl version
+}
+
+interface
+
+uses
+  Windows, Messages, Controls, Graphics, Classes,
+  BitmapImage;
+
+type
+  TBitmapButton = class(TCustomControl)
+  private
+    FFocusBorderWidthHeight: Integer;
+    FImpl: TBitmapImageImplementation;
+    FOnClick: TNotifyEvent;
+    FOnDblClick: TNotifyEvent;
+    procedure SetBackColor(Value: TColor);
+    procedure SetBitmap(Value: TBitmap);
+    procedure SetCenter(Value: Boolean);
+    procedure SetReplaceColor(Value: TColor);
+    procedure SetReplaceWithColor(Value: TColor);
+    procedure SetStretch(Value: Boolean);
+    procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
+    procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS;
+    procedure CNCommand(var Message: TWMCommand); message CN_COMMAND;
+  protected
+    procedure CreateParams(var Params: TCreateParams); override;
+    function GetPalette: HPALETTE; override;
+    procedure Paint; override;
+    procedure SetAutoSize(Value: Boolean); override;
+  public
+    constructor Create(AOwner: TComponent); override;
+    destructor Destroy; override;
+    function InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
+  published
+    property Align;
+    property Anchors;
+    property AutoSize: Boolean read FImpl.AutoSize write SetAutoSize default False;
+    property BackColor: TColor read FImpl.BackColor write SetBackColor default clNone;
+    property Caption;
+    property Center: Boolean read FImpl.Center write SetCenter default True;
+    property Enabled;
+    property ParentShowHint;
+    property Bitmap: TBitmap read FImpl.Bitmap write SetBitmap;
+    property PopupMenu;
+    property ShowHint;
+    property Stretch: Boolean read FImpl.Stretch write SetStretch default False;
+    property ReplaceColor: TColor read FImpl.ReplaceColor write SetReplaceColor default clNone;
+    property ReplaceWithColor: TColor read FImpl.ReplaceWithColor write SetReplaceWithColor default clNone;
+    property TabOrder;
+    property TabStop default True;
+    property Visible;
+    property OnClick: TNotifyEvent read FOnClick write FOnClick;
+    property OnDblClick: TNotifyEvent read FOnDblClick write FOnDblClick;
+    property OnPaint: TPaintEvent read FImpl.OnPaint write FImpl.OnPaint;
+  end;
+
+procedure Register;
+
+implementation
+
+procedure Register;
+begin
+  RegisterComponents('JR', [TBitmapButton]);
+end;
+
+constructor TBitmapButton.Create(AOwner: TComponent);
+begin
+  inherited;
+  ControlStyle := ControlStyle + [csReplicatable];
+  { Using a fixed focus border width/height to avoid design problems between systems }
+  FFocusBorderWidthHeight := 2;
+  const DoubleFBWH = 2*FFocusBorderWidthHeight;
+  FImpl.Init(Self, DoubleFBWH, DoubleFBWH);
+  Center := True;
+  TabStop := True;
+  Width := 75+DoubleFBWH;
+  Height := 25+DoubleFBWH;
+end;
+
+procedure TBitmapButton.CreateParams(var Params: TCreateParams);
+begin
+  inherited;
+  CreateSubClass(Params, 'BUTTON');
+end;
+
+destructor TBitmapButton.Destroy;
+begin
+  FImpl.DeInit;
+  inherited;
+end;
+
+function TBitmapButton.InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
+begin
+  Result := FImpl.InitializeFromIcon(HInstance, Name, BkColor, AscendingTrySizes);
+end;
+
+procedure TBitmapButton.SetAutoSize(Value: Boolean);
+begin
+  FImpl.SetAutoSize(Self, Value);
+end;
+
+procedure TBitmapButton.SetBackColor(Value: TColor);
+begin
+  FImpl.SetBackColor(Self, Value);
+end;
+
+procedure TBitmapButton.SetBitmap(Value: TBitmap);
+begin
+  FImpl.SetBitmap(Value);
+end;
+
+procedure TBitmapButton.SetCenter(Value: Boolean);
+begin
+  FImpl.SetCenter(Self, Value);
+end;
+
+procedure TBitmapButton.SetReplaceColor(Value: TColor);
+begin
+  FImpl.SetReplaceColor(Self, Value);
+end;
+
+procedure TBitmapButton.SetReplaceWithColor(Value: TColor);
+begin
+  FImpl.SetReplaceWithColor(Self, Value);
+end;
+
+procedure TBitmapButton.SetStretch(Value: Boolean);
+begin
+  FImpl.SetStretch(Self, Value);
+end;
+
+function TBitmapButton.GetPalette: HPALETTE;
+begin
+  Result := FImpl.GetPalette;
+end;
+
+procedure TBitmapButton.Paint;
+begin
+  Canvas.Font := Font;
+  Canvas.Brush.Color := Color;
+
+  var R := ClientRect;
+
+  if Focused and (SendMessage(Handle, WM_QUERYUISTATE, 0, 0) and UISF_HIDEFOCUS = 0) then begin
+    { See TBitBtn.DrawItem in Vcl.Buttons.pas }
+    Canvas.Pen.Color := clWindowFrame;
+    Canvas.Brush.Style := bsSolid;
+    Canvas.Brush.Color := clBtnFace;
+    { This might draw a focus border thinner or thicker than our FFocusBorderWidthHeight but that's okay }
+    Canvas.DrawFocusRect(R);
+  end;
+
+  InflateRect(R, -FFocusBorderWidthHeight, -FFocusBorderWidthHeight);
+
+  FImpl.Paint(Self, Canvas, R);
+end;
+
+procedure TBitmapButton.WMSetFocus(var Message: TWMSetFocus);
+begin
+  inherited;
+  Invalidate;
+end;
+
+procedure TBitmapButton.WMKillFocus(var Message: TWMKillFocus);
+begin
+  inherited;
+  Invalidate;
+end;
+
+procedure TBitmapButton.CNCommand(var Message: TWMCommand);
+begin
+  if (Message.NotifyCode = BN_CLICKED) and Assigned(FOnClick) then
+    FOnClick(Self)
+  else if (Message.NotifyCode = BN_DBLCLK) and Assigned(FOnDblClick) then
+    FOnDblClick(Self);
+end;
+
+end.

+ 240 - 157
Components/BitmapImage.pas

@@ -2,11 +2,13 @@ unit BitmapImage;
 
 {
   Inno Setup
-  Copyright (C) 1997-2019 Jordan Russell
+  Copyright (C) 1997-2025 Jordan Russell
   Portions by Martijn Laan
   For conditions of distribution and use, see LICENSE.TXT.
 
   A TImage-like component for bitmaps without the TPicture bloat
+
+  Also see TBitmapButton which is the TWinControl version
 }
 
 interface
@@ -15,25 +17,48 @@ uses
   Windows, Controls, Graphics, Classes;
 
 type
-  TBitmapImage = class(TGraphicControl)
+  TPaintEvent = procedure(Sender: TObject; Canvas: TCanvas; var ARect: TRect) of object;
+
+  TBitmapImageImplementation = record
   private
-    FAutoSize: Boolean;
-    FBackColor: TColor;
-    FBitmap: TBitmap;
-    FCenter: Boolean;
-    FReplaceColor: TColor;
-    FReplaceWithColor: TColor;
-    FStretch: Boolean;
-    FStretchedBitmap: TBitmap;
-    FStretchedBitmapValid: Boolean;
+    FControl: TControl;
+  public
+    AutoSize: Boolean;
+    AutoSizeExtraWidth, AutoSizeExtraHeight: Integer;
+    BackColor: TColor;
+    Bitmap: TBitmap;
+    Center: Boolean;
+    ReplaceColor: TColor;
+    ReplaceWithColor: TColor;
+    Stretch: Boolean;
+    StretchedBitmap: TBitmap;
+    StretchedBitmapValid: Boolean;
+    OnPaint: TPaintEvent;
+    procedure Init(const AControl: TControl; const AAutoSizeExtraWidth: Integer = 0;
+      const AAutoSizeExtraHeight: Integer = 0);
+    procedure DeInit;
+    function InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
     procedure BitmapChanged(Sender: TObject);
+    procedure SetAutoSize(Sender: TObject; Value: Boolean);
+    procedure SetBackColor(Sender: TObject; Value: TColor);
+    procedure SetBitmap(Value: TBitmap);
+    procedure SetCenter(Sender: TObject; Value: Boolean);
+    procedure SetReplaceColor(Sender: TObject; Value: TColor);
+    procedure SetReplaceWithColor(Sender: TObject; Value: TColor);
+    procedure SetStretch(Sender: TObject; Value: Boolean);
+    function GetPalette: HPALETTE;
+    procedure Paint(const Sender: TObject; const Canvas: TCanvas; var R: TRect);
+  end;
+
+  TBitmapImage = class(TGraphicControl)
+  private
+    FImpl: TBitmapImageImplementation;
     procedure SetBackColor(Value: TColor);
     procedure SetBitmap(Value: TBitmap);
     procedure SetCenter(Value: Boolean);
     procedure SetReplaceColor(Value: TColor);
     procedure SetReplaceWithColor(Value: TColor);
     procedure SetStretch(Value: Boolean);
-    function GetBitmap: TBitmap;
   protected
     function GetPalette: HPALETTE; override;
     procedure Paint; override;
@@ -45,19 +70,19 @@ type
   published
     property Align;
     property Anchors;
-    property AutoSize: Boolean read FAutoSize write SetAutoSize default False;
-    property BackColor: TColor read FBackColor write SetBackColor default clBtnFace;
-    property Center: Boolean read FCenter write SetCenter default False;
+    property AutoSize: Boolean read FImpl.AutoSize write SetAutoSize default False;
+    property BackColor: TColor read FImpl.BackColor write SetBackColor default clBtnFace;
+    property Center: Boolean read FImpl.Center write SetCenter default False;
     property DragCursor;
     property DragMode;
     property Enabled;
     property ParentShowHint;
-    property Bitmap: TBitmap read GetBitmap write SetBitmap;
+    property Bitmap: TBitmap read FImpl.Bitmap write SetBitmap;
     property PopupMenu;
     property ShowHint;
-    property Stretch: Boolean read FStretch write SetStretch default False;
-    property ReplaceColor: TColor read FReplaceColor write SetReplaceColor default clNone;
-    property ReplaceWithColor: TColor read FReplaceWithColor write SetReplaceWithColor default clNone;
+    property Stretch: Boolean read FImpl.Stretch write SetStretch default False;
+    property ReplaceColor: TColor read FImpl.ReplaceColor write SetReplaceColor default clNone;
+    property ReplaceWithColor: TColor read FImpl.ReplaceWithColor write SetReplaceWithColor default clNone;
     property Visible;
     property OnClick;
     property OnDblClick;
@@ -67,6 +92,7 @@ type
     property OnMouseDown;
     property OnMouseMove;
     property OnMouseUp;
+    property OnPaint: TPaintEvent read FImpl.OnPaint write FImpl.OnPaint;
     property OnStartDrag;
   end;
 
@@ -75,51 +101,68 @@ procedure Register;
 implementation
 
 uses
-  Math, Resample;
+  SysUtils, Math, Resample;
 
 procedure Register;
 begin
   RegisterComponents('JR', [TBitmapImage]);
 end;
 
-function TBitmapImage.InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
-var
-  Flags: Cardinal;
-  Handle: THandle;
-  Icon: TIcon;
-  I, Size: Integer;
+{ TBitmapImageImplementation }
+
+procedure TBitmapImageImplementation.Init(const AControl: TControl;
+  const AAutoSizeExtraWidth, AAutoSizeExtraHeight: Integer);
+begin
+  FControl := AControl;
+  AutoSizeExtraWidth := AAutoSizeExtraWidth;
+  AutoSizeExtraHeight := AAutoSizeExtraHeight;
+  Bitmap := TBitmap.Create;
+  Bitmap.OnChange := BitmapChanged;
+  BackColor := clNone;
+  ReplaceColor := clNone;
+  ReplaceWithColor := clNone;
+  StretchedBitmap := TBitmap.Create;
+end;
+
+procedure TBitmapImageImplementation.DeInit;
+begin
+  FreeAndNil(StretchedBitmap);
+  FreeAndNil(Bitmap);
+end;
+
+function TBitmapImageImplementation.InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
 begin
   { Find the largest regular icon size smaller than the scaled image }
-  Size := 0;
-  for I := Length(AscendingTrySizes)-1 downto 0 do begin
-    if (Width >= AscendingTrySizes[I]) and (Height >= AscendingTrySizes[I]) then begin
+  var Size := 0;
+  for var I := Length(AscendingTrySizes)-1 downto 0 do begin
+    if (FControl.Width >= AscendingTrySizes[I]) and (FControl.Height >= AscendingTrySizes[I]) then begin
       Size := AscendingTrySizes[I];
       Break;
     end;
   end;
   if Size = 0 then
-    Size := Min(Width, Height);
+    Size := Min(FControl.Width, FControl.Height);
 
   { Load the desired icon }
-  Flags := LR_DEFAULTCOLOR;
+  var Flags := LR_DEFAULTCOLOR;
   if Instance = 0 then
     Flags := Flags or LR_LOADFROMFILE;
-  Handle := LoadImage(Instance, Name, IMAGE_ICON, Size, Size, Flags);
+  var Handle := LoadImage(Instance, Name, IMAGE_ICON, Size, Size, Flags);
   if Handle = 0 then
     Handle := LoadImage(Instance, Name, IMAGE_ICON, 0, 0, Flags);
   if Handle <> 0 then begin
-    Icon := TIcon.Create;
+    const Icon = TIcon.Create;
     try
       Icon.Handle := Handle;
 
       { Set sizes (overrides any scaling) }
-      Width := Icon.Width;
-      Height := Icon.Height;
+      FControl.Width := Icon.Width;
+      FControl.Height := Icon.Height;
 
       { Draw icon into bitmap }
       Bitmap.Canvas.Brush.Color := BkColor;
-      Bitmap.Width := Width;
-      Bitmap.Height := Height;
+      Bitmap.Width := FControl.Width;
+      Bitmap.Height := FControl.Height;
       Bitmap.Canvas.Draw(0, 0, Icon);
 
       Result := True;
@@ -130,173 +173,213 @@ begin
     Result := False;
 end;
 
+procedure TBitmapImageImplementation.BitmapChanged(Sender: TObject);
+begin
+  StretchedBitmapValid := False;
+  if AutoSize and (Bitmap.Width > 0) and (Bitmap.Height > 0) then
+    FControl.SetBounds(FControl.Left, FControl.Top, Bitmap.Width + AutoSizeExtraWidth,
+      Bitmap.Height + AutoSizeExtraHeight);
+  if (Bitmap.Width >= FControl.Width) and (Bitmap.Height >= FControl.Height) then
+    FControl.ControlStyle := FControl.ControlStyle + [csOpaque] - [csParentBackground]
+  else
+    FControl.ControlStyle := FControl.ControlStyle - [csOpaque] + [csParentBackground];
+  FControl.Invalidate;
+end;
+
+procedure TBitmapImageImplementation.SetAutoSize(Sender: TObject; Value: Boolean);
+begin
+  AutoSize := Value;
+  BitmapChanged(Sender);
+end;
+
+procedure TBitmapImageImplementation.SetBackColor(Sender: TObject; Value: TColor);
+begin
+  if BackColor <> Value then begin
+    BackColor := Value;
+    BitmapChanged(Sender);
+  end;
+end;
+
+procedure TBitmapImageImplementation.SetBitmap(Value: TBitmap);
+begin
+  Bitmap.Assign(Value);
+end;
+
+procedure TBitmapImageImplementation.SetCenter(Sender: TObject; Value: Boolean);
+begin
+  if Center <> Value then begin
+    Center := Value;
+    BitmapChanged(Sender);
+  end;
+end;
+
+procedure TBitmapImageImplementation.SetReplaceColor(Sender: TObject; Value: TColor);
+begin
+  if ReplaceColor <> Value then begin
+    ReplaceColor := Value;
+    BitmapChanged(Sender);
+  end;
+end;
+
+procedure TBitmapImageImplementation.SetReplaceWithColor(Sender: TObject; Value: TColor);
+begin
+  if ReplaceWithColor <> Value then begin
+    ReplaceWithColor := Value;
+    BitmapChanged(Sender);
+  end;
+end;
+
+procedure TBitmapImageImplementation.SetStretch(Sender: TObject; Value: Boolean);
+begin
+  if Stretch <> Value then begin
+    Stretch := Value;
+    StretchedBitmap.Assign(nil);
+    BitmapChanged(Sender);
+  end;
+end;
+
+function TBitmapImageImplementation.GetPalette: HPALETTE;
+begin
+  Result := Bitmap.Palette;
+end;
+
+procedure TBitmapImageImplementation.Paint(const Sender: TObject; const Canvas: TCanvas; var R: TRect);
+begin
+  const Is32bit = (Bitmap.PixelFormat = pf32bit) and
+    (Bitmap.AlphaFormat in [afDefined, afPremultiplied]);
+
+  var W, H: Integer;
+  var Bmp: TBitmap;
+  if Stretch then begin
+    W := R.Width;
+    H := R.Height;
+    Bmp := StretchedBitmap;
+    if not StretchedBitmapValid or (StretchedBitmap.Width <> W) or
+        (StretchedBitmap.Height <> H) then begin
+      StretchedBitmapValid := True;
+      if (Bitmap.Width = W) and (Bitmap.Height = H) then
+        StretchedBitmap.Assign(Bitmap)
+      else begin
+        StretchedBitmap.Assign(nil);
+        if not StretchBmp(Bitmap, StretchedBitmap, W, H, Is32bit) then begin
+          if Is32bit then begin
+            StretchedBitmapValid := False;
+            Bmp := Bitmap;
+          end else begin
+            StretchedBitmap.Palette := CopyPalette(Bitmap.Palette);
+            StretchedBitmap.Width := W;
+            StretchedBitmap.Height := H;
+            StretchedBitmap.Canvas.StretchDraw(Rect(0, 0, W, H), Bitmap);
+          end;
+        end;
+      end;
+    end;
+  end else begin
+    Bmp := Bitmap;
+    W := Bmp.Width;
+    H := Bmp.Height;
+  end;
+
+  if (BackColor <> clNone) and (Is32Bit or (Bmp.Width < FControl.Width) or (Bmp.Height < FControl.Height)) then begin
+    Canvas.Brush.Style := bsSolid;
+    Canvas.Brush.Color := BackColor;
+    Canvas.FillRect(R);
+  end;
+
+  if csDesigning in FControl.ComponentState then begin
+    Canvas.Pen.Style := psDash;
+    Canvas.Brush.Style := bsClear;
+    Canvas.Rectangle(0, 0, FControl.Width, FControl.Height);
+  end;
+
+  var X := R.Left;
+  var Y := R.Top;
+  if Center then begin
+    Inc(X, (R.Width - W) div 2);
+    if X < 0 then
+      X := 0;
+    Inc(Y, (R.Height - H) div 2);
+    if Y < 0 then
+      Y := 0;
+  end;
+
+  if not Is32bit and (ReplaceColor <> clNone) and (ReplaceWithColor <> clNone) then begin
+    Canvas.Brush.Color := ReplaceWithColor;
+    Canvas.BrushCopy(Rect(X, Y, X + W, Y + H), Bmp, Rect(0, 0, Bmp.Width, Bmp.Height), ReplaceColor);
+  end else
+    Canvas.Draw(X, Y, Bmp);
+
+  if Assigned(OnPaint) then
+    OnPaint(Sender, Canvas, R);
+end;
+
+{ TBitmapImage }
+
 constructor TBitmapImage.Create(AOwner: TComponent);
 begin
-  inherited Create(AOwner);
+  inherited;
   ControlStyle := ControlStyle + [csReplicatable];
-  FBackColor := clBtnFace;
-  FBitmap := TBitmap.Create;
-  FBitmap.OnChange := BitmapChanged;
-  FReplaceColor := clNone;
-  FReplaceWithColor := clNone;
-  FStretchedBitmap := TBitmap.Create;
-  Height := 105;
+  FImpl.Init(Self);
+  FImpl.BackColor := clBtnFace;
   Width := 105;
+  Height := 105;
 end;
 
 destructor TBitmapImage.Destroy;
 begin
-  FStretchedBitmap.Free;
-  FBitmap.Free;
-  inherited Destroy;
+  FImpl.DeInit;
+  inherited;
 end;
 
-procedure TBitmapImage.BitmapChanged(Sender: TObject);
+function TBitmapImage.InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
 begin
-  FStretchedBitmapValid := False;
-  if FAutoSize and (FBitmap.Width > 0) and (FBitmap.Height > 0) then
-    SetBounds(Left, Top, FBitmap.Width, FBitmap.Height);
-  if (FBitmap.Width >= Width) and (FBitmap.Height >= Height) then
-    ControlStyle := ControlStyle + [csOpaque]
-  else
-    ControlStyle := ControlStyle - [csOpaque];
-  Invalidate;
+  Result := FImpl.InitializeFromIcon(HInstance, Name, BkColor, AscendingTrySizes);
 end;
 
 procedure TBitmapImage.SetAutoSize(Value: Boolean);
 begin
-  FAutoSize := Value;
-  BitmapChanged(Self);
+  FImpl.SetAutoSize(Self, Value);
 end;
 
 procedure TBitmapImage.SetBackColor(Value: TColor);
 begin
-  if FBackColor <> Value then begin
-    FBackColor := Value;
-    BitmapChanged(Self);
-  end;
+  FImpl.SetBackColor(Self, Value);
 end;
 
 procedure TBitmapImage.SetBitmap(Value: TBitmap);
 begin
-  FBitmap.Assign(Value);
+  FImpl.SetBitmap(Value);
 end;
 
 procedure TBitmapImage.SetCenter(Value: Boolean);
 begin
-  if FCenter <> Value then begin
-    FCenter := Value;
-    BitmapChanged(Self);
-  end;
+  FImpl.SetCenter(Self, Value);
 end;
 
 procedure TBitmapImage.SetReplaceColor(Value: TColor);
 begin
-  if FReplaceColor <> Value then begin
-    FReplaceColor := Value;
-    BitmapChanged(Self);
-  end;
+  FImpl.SetReplaceColor(Self, Value);
 end;
 
 procedure TBitmapImage.SetReplaceWithColor(Value: TColor);
 begin
-  if FReplaceWithColor <> Value then begin
-    FReplaceWithColor := Value;
-    BitmapChanged(Self);
-  end;
+  FImpl.SetReplaceWithColor(Self, Value);
 end;
 
 procedure TBitmapImage.SetStretch(Value: Boolean);
 begin
-  if FStretch <> Value then begin
-    FStretch := Value;
-    FStretchedBitmap.Assign(nil);
-    BitmapChanged(Self);
-  end;
-end;
-
-function TBitmapImage.GetBitmap: TBitmap;
-begin
-  Result := FBitmap;
+  FImpl.SetStretch(Self, Value);
 end;
 
 function TBitmapImage.GetPalette: HPALETTE;
 begin
-  Result := FBitmap.Palette;
+  Result := FImpl.GetPalette;
 end;
 
 procedure TBitmapImage.Paint;
-var
-  R: TRect;
-  Bmp: TBitmap;
-  X, Y, W, H: Integer;
-  Is32bit: Boolean;
 begin
-  with Canvas do begin
-    R := ClientRect;
-    Is32bit := (FBitmap.PixelFormat = pf32bit) and
-      (FBitmap.AlphaFormat in [afDefined, afPremultiplied]);
-
-    if Stretch then begin
-      W := R.Right;
-      H := R.Bottom;
-      Bmp := FStretchedBitmap;
-      if not FStretchedBitmapValid or (FStretchedBitmap.Width <> W) or
-         (FStretchedBitmap.Height <> H) then begin
-        FStretchedBitmapValid := True;
-        if (FBitmap.Width = W) and (FBitmap.Height = H) then
-          FStretchedBitmap.Assign(FBitmap)
-        else begin
-          FStretchedBitmap.Assign(nil);
-          if not StretchBmp(FBitmap, FStretchedBitmap, W, H, Is32bit) then begin
-            if Is32bit then begin
-              FStretchedBitmapValid := False;
-              Bmp := FBitmap;
-            end else begin
-              FStretchedBitmap.Palette := CopyPalette(FBitmap.Palette);
-              FStretchedBitmap.Width := W;
-              FStretchedBitmap.Height := H;
-              FStretchedBitmap.Canvas.StretchDraw(R, FBitmap);
-            end;
-          end;
-        end;
-      end;
-    end else begin
-      Bmp := FBitmap;
-      W := Bmp.Width;
-      H := Bmp.Height;
-    end;
-
-    if (FBackColor <> clNone) and (Is32Bit or (Bmp.Width < Width) or (Bmp.Height < Height)) then begin
-      Brush.Style := bsSolid;
-      Brush.Color := FBackColor;
-      FillRect(R);
-    end;
-
-    if csDesigning in ComponentState then begin
-      Pen.Style := psDash;
-      Brush.Style := bsClear;
-      Rectangle(0, 0, Width, Height);
-    end;
-
-    if Center then begin
-      X := R.Left + ((R.Right - R.Left) - W) div 2;
-      if X < 0 then
-        X := 0;
-      Y := R.Top + ((R.Bottom - R.Top) - H) div 2;
-      if Y < 0 then
-        Y := 0;
-    end else begin
-      X := 0;
-      Y := 0;
-    end;
-
-    if not Is32bit and (FReplaceColor <> clNone) and (FReplaceWithColor <> clNone) then begin
-      Brush.Color := FReplaceWithColor;
-      BrushCopy(Rect(X, Y, X + W, Y + H), Bmp, Rect(0, 0, Bmp.Width, Bmp.Height), FReplaceColor);
-    end else
-      Draw(X, Y, Bmp);
-  end;
+  var R := ClientRect;
+  FImpl.Paint(Self, Canvas, R);
 end;
 
 end.

+ 1 - 0
Components/Components.dpk

@@ -38,6 +38,7 @@ requires
 
 contains
   BidiCtrls in 'BidiCtrls.pas',
+  BitmapButton in 'BitmapButton.pas',
   BitmapImage in 'BitmapImage.pas',
   DropListBox in 'DropListBox.pas',
   FolderTreeView in 'FolderTreeView.pas',

+ 36 - 14
Examples/CodeClasses.iss

@@ -27,11 +27,6 @@ begin
   MsgBox('You clicked the button!', mbInformation, mb_Ok);
 end;
 
-procedure BitmapImageOnClick(Sender: TObject);
-begin
-  MsgBox('You clicked the image!', mbInformation, mb_Ok);
-end;
-
 procedure FormButtonOnClick(Sender: TObject);
 var
   Form: TSetupForm;
@@ -132,6 +127,7 @@ var
   CheckListBox, CheckListBox2: TNewCheckListBox;
   FolderTreeView: TFolderTreeView;
   BitmapImage, BitmapImage2, BitmapImage3: TBitmapImage;
+  BitmapButton, BitmapButton2: TBitmapButton;
   BitmapFileName: String;
   RichEditViewer: TRichEditViewer;
 begin
@@ -353,32 +349,58 @@ begin
   BitmapImage := TBitmapImage.Create(Page);
   BitmapImage.AutoSize := True;
   BitmapImage.Bitmap.LoadFromFile(BitmapFileName);
-  BitmapImage.Cursor := crHand;
-  BitmapImage.OnClick := @BitmapImageOnClick;
   BitmapImage.Parent := Page.Surface;
 
   BitmapImage2 := TBitmapImage.Create(Page);
-  BitmapImage2.BackColor := $400000;
+  BitmapImage2.BackColor := clNone;
   BitmapImage2.Bitmap := BitmapImage.Bitmap;
   BitmapImage2.Center := True;
   BitmapImage2.Left := BitmapImage.Width + 10;
-  BitmapImage2.Height := 2*BitmapImage.Height;
   BitmapImage2.Width := 2*BitmapImage.Width;
-  BitmapImage2.Cursor := crHand;
-  BitmapImage2.OnClick := @BitmapImageOnClick;
+  BitmapImage2.Height := 2*BitmapImage.Height;
   BitmapImage2.Parent := Page.Surface;
 
   BitmapImage3 := TBitmapImage.Create(Page);
   BitmapImage3.Bitmap := BitmapImage.Bitmap;
   BitmapImage3.Stretch := True;
   BitmapImage3.Left := 3*BitmapImage.Width + 20;
-  BitmapImage3.Height := 4*BitmapImage.Height;
   BitmapImage3.Width := 4*BitmapImage.Width;
+  BitmapImage3.Height := 4*BitmapImage.Height;
   BitmapImage3.Anchors := [akLeft, akTop, akRight, akBottom];
-  BitmapImage3.Cursor := crHand;
-  BitmapImage3.OnClick := @BitmapImageOnClick;
   BitmapImage3.Parent := Page.Surface;
 
+  { TBitmapButton - Always has a 2 pixel margin around the image, used to
+    display a focus rectangle. Other changes compared to TBitmapImage are:
+    • Has a Caption property which should always be set
+    • Center defaults to True
+    • BackColor defaults to clNone }
+
+  Page := CreateCustomPage(Page.ID, 'Custom wizard page controls', 'TBitmapButton (Press Alt to see focus rectangle)');
+  
+  BitmapButton := TBitmapButton.Create(Page);
+  BitmapButton.AutoSize := True;
+  BitmapButton.Bitmap := BitmapImage.Bitmap;
+  BitmapButton.Caption := 'Show Message'; { For accessibility }
+  BitmapButton.Hint := 'TBitmapButton is an accessible version of TBitmapImage';
+  BitmapButton.ShowHint := True;
+  BitmapButton.Width := 2*BitmapButton.Width;
+  BitmapButton.Cursor := crHand;
+  BitmapButton.OnClick := @ButtonOnClick;
+  BitmapButton.Parent := Page.Surface;
+
+  BitmapButton2 := TBitmapButton.Create(Page);
+  BitmapButton2.BackColor := $400000;
+  BitmapButton2.Bitmap := BitmapImage.Bitmap;
+  BitmapButton2.Caption := BitmapButton.Caption;
+  BitmapButton2.Hint := BitmapButton.Hint;
+  BitmapButton2.ShowHint := True;
+  BitmapButton2.Left := BitmapButton.Width + 10;
+  BitmapButton2.Width := 2*BitmapButton.Width;
+  BitmapButton2.Height := 2*BitmapButton.Height;
+  BitmapButton2.Cursor := crHand;
+  BitmapButton2.OnClick := @ButtonOnClick;
+  BitmapButton2.Parent := Page.Surface;
+
   { TRichViewer }
 
   Page := CreateCustomPage(Page.ID, 'Custom wizard page controls', 'TRichViewer');

+ 14 - 0
ISHelp/isxclasses.pas

@@ -667,6 +667,20 @@ TStartMenuFolderTreeView = class(TCustomFolderTreeView)
   property OnRename: TFolderRenameEvent; read write;
 end;
 
+TBitmapButton = class(TCustomControl)
+  property Anchors: TAnchors; read write;
+  property AutoSize: Boolean; read write;
+  property BackColor: TColor; read write;
+  property Caption: Boolean; read write;
+  property Center: Boolean; read write;
+  property Bitmap: TBitmap; read write;
+  property ReplaceColor: TColor; read write;
+  property ReplaceWithColor: TColor; read write;
+  property Stretch: Boolean; read write;
+  property OnClick: TNotifyEvent; read write;
+  property OnDblClick: TNotifyEvent; read write;
+end;
+
 TBitmapImage = class(TGraphicControl)
   property Anchors: TAnchors; read write;
   property AutoSize: Boolean; read write;

+ 23 - 23
ISHelp/isxclasses_wordlists_generated.pas

@@ -16,29 +16,29 @@ var
 
   PascalTypes_Isxclasses: array of AnsiString = [
     'HBITMAP', 'TAlign', 'TAlignment', 'TAlphaFormat', 'TAnchorKind', 'TAnchors', 'TBevel',
-    'TBevelKind', 'TBevelShape', 'TBevelStyle', 'TBevelWidth', 'TBitmap', 'TBitmapImage',
-    'TBorderIcon', 'TBorderIcons', 'TBorderStyle', 'TBorderWidth', 'TBrush', 'TBrushStyle',
-    'TButton', 'TButtonControl', 'TCanvas', 'TCheckBox', 'TCheckBoxState', 'TCheckItemOperation',
-    'TCloseAction', 'TCloseEvent', 'TCloseQueryEvent', 'TColor', 'TComboBox', 'TComboBoxStyle',
-    'TComponent', 'TConstraintSize', 'TControl', 'TCursor', 'TCustomCheckBox', 'TCustomComboBox',
-    'TCustomControl', 'TCustomEdit', 'TCustomFolderTreeView', 'TCustomLabel', 'TCustomLinkLabel',
-    'TCustomListBox', 'TCustomMemo', 'TCustomPanel', 'TDownloadWizardPage', 'TDuplicates',
-    'TEdit', 'TEditCharCase', 'TEShiftState', 'TExtractionWizardPage', 'TFileStream', 'TFolderRenameEvent',
-    'TFolderTreeView', 'TFont', 'TFontStyle', 'TFontStyles', 'TForm', 'TFormBorderStyle',
-    'TFormStyle', 'TGraphic', 'TGraphicControl', 'TGraphicsObject', 'THandleStream', 'TInputDirWizardPage',
-    'TInputFileWizardPage', 'TInputOptionWizardPage', 'TInputQueryWizardPage', 'TKeyEvent',
-    'TKeyPressEvent', 'TLabel', 'TLinkLabel', 'TListBox', 'TListBoxStyle', 'TMemo', 'TNewButton',
-    'TNewCheckBox', 'TNewCheckListBox', 'TNewComboBox', 'TNewEdit', 'TNewLinkLabel', 'TNewListBox',
-    'TNewMemo', 'TNewNotebook', 'TNewNotebookPage', 'TNewProgressBar', 'TNewProgressBarState',
-    'TNewProgressBarStyle', 'TNewRadioButton', 'TNewStaticText', 'TNotifyEvent', 'TObject',
-    'TOutputMarqueeProgressWizardPage', 'TOutputMsgMemoWizardPage', 'TOutputMsgWizardPage',
-    'TOutputProgressWizardPage', 'TPanel', 'TPanelBevel', 'TPasswordEdit', 'TPen', 'TPenMode',
-    'TPenStyle', 'TPersistent', 'TPopupMode', 'TPosition', 'TRadioButton', 'TRichEditViewer',
-    'TScrollingWinControl', 'TScrollStyle', 'TSetupForm', 'TShiftState', 'TSizeConstraints',
-    'TStartMenuFolderTreeView', 'TStream', 'TStringList', 'TStrings', 'TStringStream',
-    'TSysLinkEvent', 'TSysLinkType', 'TUIStateForm', 'TUninstallProgressForm', 'TWinControl',
-    'TWizardForm', 'TWizardPage', 'TWizardPageButtonEvent', 'TWizardPageCancelEvent', 'TWizardPageNotifyEvent',
-    'TWizardPageShouldSkipEvent'
+    'TBevelKind', 'TBevelShape', 'TBevelStyle', 'TBevelWidth', 'TBitmap', 'TBitmapButton',
+    'TBitmapImage', 'TBorderIcon', 'TBorderIcons', 'TBorderStyle', 'TBorderWidth', 'TBrush',
+    'TBrushStyle', 'TButton', 'TButtonControl', 'TCanvas', 'TCheckBox', 'TCheckBoxState',
+    'TCheckItemOperation', 'TCloseAction', 'TCloseEvent', 'TCloseQueryEvent', 'TColor',
+    'TComboBox', 'TComboBoxStyle', 'TComponent', 'TConstraintSize', 'TControl', 'TCursor',
+    'TCustomCheckBox', 'TCustomComboBox', 'TCustomControl', 'TCustomEdit', 'TCustomFolderTreeView',
+    'TCustomLabel', 'TCustomLinkLabel', 'TCustomListBox', 'TCustomMemo', 'TCustomPanel',
+    'TDownloadWizardPage', 'TDuplicates', 'TEdit', 'TEditCharCase', 'TEShiftState', 'TExtractionWizardPage',
+    'TFileStream', 'TFolderRenameEvent', 'TFolderTreeView', 'TFont', 'TFontStyle', 'TFontStyles',
+    'TForm', 'TFormBorderStyle', 'TFormStyle', 'TGraphic', 'TGraphicControl', 'TGraphicsObject',
+    'THandleStream', 'TInputDirWizardPage', 'TInputFileWizardPage', 'TInputOptionWizardPage',
+    'TInputQueryWizardPage', 'TKeyEvent', 'TKeyPressEvent', 'TLabel', 'TLinkLabel', 'TListBox',
+    'TListBoxStyle', 'TMemo', 'TNewButton', 'TNewCheckBox', 'TNewCheckListBox', 'TNewComboBox',
+    'TNewEdit', 'TNewLinkLabel', 'TNewListBox', 'TNewMemo', 'TNewNotebook', 'TNewNotebookPage',
+    'TNewProgressBar', 'TNewProgressBarState', 'TNewProgressBarStyle', 'TNewRadioButton',
+    'TNewStaticText', 'TNotifyEvent', 'TObject', 'TOutputMarqueeProgressWizardPage', 'TOutputMsgMemoWizardPage',
+    'TOutputMsgWizardPage', 'TOutputProgressWizardPage', 'TPanel', 'TPanelBevel', 'TPasswordEdit',
+    'TPen', 'TPenMode', 'TPenStyle', 'TPersistent', 'TPopupMode', 'TPosition', 'TRadioButton',
+    'TRichEditViewer', 'TScrollingWinControl', 'TScrollStyle', 'TSetupForm', 'TShiftState',
+    'TSizeConstraints', 'TStartMenuFolderTreeView', 'TStream', 'TStringList', 'TStrings',
+    'TStringStream', 'TSysLinkEvent', 'TSysLinkType', 'TUIStateForm', 'TUninstallProgressForm',
+    'TWinControl', 'TWizardForm', 'TWizardPage', 'TWizardPageButtonEvent', 'TWizardPageCancelEvent',
+    'TWizardPageNotifyEvent', 'TWizardPageShouldSkipEvent'
   ];
 
   PascalEnumValues_Isxclasses: array of AnsiString = [

+ 6 - 0
ISHelp/isxfunc.xml

@@ -2818,6 +2818,12 @@ end;</pre></example>
         <prototype>function ScaleY(Y: Integer): Integer;</prototype>
         <description><p>Takes a Y coordinate or height and returns it scaled to fit the size of the current dialog font. If the dialog font is 8-point MS Sans Serif and the user is running Windows in 100% DPI (96 dpi), then Y is returned unchanged.</p></description>
       </function>
+      <function>
+        <name>InitializeBitmapButtonFromIcon</name>
+        <prototype>function InitializeBitmapButtonFromIcon(const BitmapButton: TBitmapButton; const IconFilename: String; const BkColor: TColor; const AscendingTrySizes: TArrayOfInteger): Boolean;</prototype>
+        <description><p>Initializes the given bitmap button with an icon from the given icon file using the given background color for transparent parts.</p>
+<p>See <link topic="isxfunc_InitializeBitmapImageFromIcon">InitializeBitmapImageFromIcon</link> for more information.</p></description>
+      </function>
       <function>
         <name>InitializeBitmapImageFromIcon</name>
         <prototype>function InitializeBitmapImageFromIcon(const BitmapImage: TBitmapImage; const IconFilename: String; const BkColor: TColor; const AscendingTrySizes: TArrayOfInteger): Boolean;</prototype>

+ 3 - 0
Projects/Compil32.dpr

@@ -47,6 +47,8 @@ uses
   NewStaticText in '..\Components\NewStaticText.pas',
   BidiUtils in '..\Components\BidiUtils.pas',
   DropListBox in '..\Components\DropListBox.pas',
+  BitmapButton in '..\Components\BitmapButton.pas',
+  BitmapImage in '..\Components\BitmapImage.pas',
   NewCheckListBox in '..\Components\NewCheckListBox.pas',
   NewNotebook in '..\Components\NewNotebook.pas',
   TaskbarProgressFunc in '..\Components\TaskbarProgressFunc.pas',
@@ -73,6 +75,7 @@ uses
   ECDSA in '..\Components\ECDSA.pas',
   ISSigFunc in '..\Components\ISSigFunc.pas',
   StringScanner in '..\Components\StringScanner.pas',
+  Resample in '..\Components\Resample.pas',
   VCL.Styles,
   VCL.Themes;
 

+ 3 - 0
Projects/Compil32.dproj

@@ -127,6 +127,8 @@
         <DCCReference Include="..\Components\NewStaticText.pas"/>
         <DCCReference Include="..\Components\BidiUtils.pas"/>
         <DCCReference Include="..\Components\DropListBox.pas"/>
+        <DCCReference Include="..\Components\BitmapButton.pas"/>
+        <DCCReference Include="..\Components\BitmapImage.pas"/>
         <DCCReference Include="..\Components\NewCheckListBox.pas"/>
         <DCCReference Include="..\Components\NewNotebook.pas"/>
         <DCCReference Include="..\Components\TaskbarProgressFunc.pas"/>
@@ -159,6 +161,7 @@
         <DCCReference Include="..\Components\ECDSA.pas"/>
         <DCCReference Include="..\Components\ISSigFunc.pas"/>
         <DCCReference Include="..\Components\StringScanner.pas"/>
+        <DCCReference Include="..\Components\Resample.pas"/>
         <BuildConfiguration Include="Base">
             <Key>Base</Key>
         </BuildConfiguration>

+ 1 - 0
Projects/Setup.dpr

@@ -75,6 +75,7 @@ uses
   BidiUtils in '..\Components\BidiUtils.pas',
   PathFunc in '..\Components\PathFunc.pas',
   BidiCtrls in '..\Components\BidiCtrls.pas',
+  BitmapButton in '..\Components\BitmapButton.pas',
   BitmapImage in '..\Components\BitmapImage.pas',
   FolderTreeView in '..\Components\FolderTreeView.pas',
   NewCheckListBox in '..\Components\NewCheckListBox.pas',

+ 1 - 0
Projects/Setup.dproj

@@ -148,6 +148,7 @@
         <DCCReference Include="..\Components\BidiUtils.pas"/>
         <DCCReference Include="..\Components\PathFunc.pas"/>
         <DCCReference Include="..\Components\BidiCtrls.pas"/>
+        <DCCReference Include="..\Components\BitmapButton.pas"/>
         <DCCReference Include="..\Components\BitmapImage.pas"/>
         <DCCReference Include="..\Components\FolderTreeView.pas"/>
         <DCCReference Include="..\Components\NewCheckListBox.pas"/>

+ 20 - 1
Projects/Src/Compiler.ScriptClasses.pas

@@ -229,13 +229,31 @@ begin
   end;
 end;
 
-procedure RegisterBitmapImage_C(Cl: TPSPascalCompiler);
+procedure RegisterBitmapButton_C(Cl: TPSPascalCompiler);
 begin
   Cl.AddTypeS('TAlphaFormat', '(afIgnored, afDefined, afPremultiplied)');
   with Cl.FindClass('TBitmap') do
   begin
     RegisterProperty('AlphaFormat', 'TAlphaFormat', iptrw);
   end;
+  with Cl.AddClassN(CL.FindClass('TCustomControl'),'TBitmapButton') do
+  begin
+    RegisterProperty('Anchors', 'TAnchors', iptrw);
+    RegisterProperty('AutoSize', 'Boolean', iptrw);
+    RegisterProperty('BackColor', 'TColor', iptrw);
+    RegisterProperty('Caption', 'String', iptrw);
+    RegisterProperty('Center', 'Boolean', iptrw);
+    RegisterProperty('Bitmap', 'TBitmap', iptrw);
+    RegisterProperty('ReplaceColor', 'TColor', iptrw);
+    RegisterProperty('ReplaceWithColor', 'TColor', iptrw);
+    RegisterProperty('Stretch', 'Boolean', iptrw);
+    RegisterProperty('OnClick', 'TNotifyEvent', iptrw);
+    RegisterProperty('OnDblClick', 'TNotifyEvent', iptrw);
+  end;
+end;
+
+procedure RegisterBitmapImage_C(Cl: TPSPascalCompiler);
+begin
   with Cl.AddClassN(CL.FindClass('TGraphicControl'),'TBitmapImage') do
   begin
     RegisterProperty('Anchors', 'TAnchors', iptrw);
@@ -663,6 +681,7 @@ begin
   RegisterCustomFolderTreeView_C(Cl);
   RegisterFolderTreeView_C(Cl);
   RegisterStartMenuFolderTreeView_C(Cl);
+  RegisterBitmapButton_C(Cl);
   RegisterBitmapImage_C(Cl);
   RegisterBidiCtrls_C(Cl);
 

+ 9 - 9
Projects/Src/IDE.InputQueryMemoForm.dfm

@@ -27,15 +27,15 @@ object InputQueryMemoForm: TInputQueryMemoForm
     Caption = '...'
     FocusControl = ValueControl
   end
-  object DocImage: TImage
-    Left = 8
-    Top = 115
-    Width = 16
-    Height = 16
+  object DocBitBtn: TBitmapButton
+    Left = 6
+    Top = 113
+    Width = 20
+    Height = 20
     Cursor = crHandPoint
     Anchors = [akLeft, akBottom]
-    AutoSize = True
-    Transparent = True
+    Caption = 'Help'
+    TabOrder = 1
   end
   object OKButton: TButton
     Left = 421
@@ -46,7 +46,7 @@ object InputQueryMemoForm: TInputQueryMemoForm
     Caption = 'OK'
     Default = True
     ModalResult = 1
-    TabOrder = 1
+    TabOrder = 2
   end
   object CancelButton: TButton
     Left = 501
@@ -57,7 +57,7 @@ object InputQueryMemoForm: TInputQueryMemoForm
     Cancel = True
     Caption = 'Cancel'
     ModalResult = 2
-    TabOrder = 2
+    TabOrder = 3
   end
   object ValueControl: TMemo
     Left = 279

+ 12 - 12
Projects/Src/IDE.InputQueryMemoForm.pas

@@ -2,7 +2,7 @@ unit IDE.InputQueryMemoForm;
 
 {
   Inno Setup
-  Copyright (C) 1997-2020 Jordan Russell
+  Copyright (C) 1997-2025 Jordan Russell
   Portions by Martijn Laan
   For conditions of distribution and use, see LICENSE.TXT.
 
@@ -14,7 +14,7 @@ unit IDE.InputQueryMemoForm;
 interface
 
 uses
-  Classes, Controls, StdCtrls, UIStateForm, Vcl.ExtCtrls;
+  Classes, Controls, StdCtrls, UIStateForm, ExtCtrls, BitmapButton;
 
 type
   TInputQueryMemoForm = class(TUIStateForm)
@@ -22,7 +22,7 @@ type
     CancelButton: TButton;
     PromptLabel: TLabel;
     ValueControl: TMemo;
-    DocImage: TImage;
+    DocBitBtn: TBitmapButton;
     procedure FormCreate(Sender: TObject);
     procedure ValueControlKeyPress(Sender: TObject; var Key: Char);
     procedure ValueControlChange(Sender: TObject);
@@ -36,16 +36,16 @@ type
     procedure SetPrompt(const APrompt: String);
     procedure SetValue(const AValue: String);
     procedure UpdateImages;
-    procedure SetDocImageClick(const Value: TNotifyEvent);
+    procedure SetDocBitBtnClick(const Value: TNotifyEvent);
   public
-    property DocImageClick: TNotifyEvent write SetDocImageClick;
+    property DocBitBtnClick: TNotifyEvent write SetDocBitBtnClick;
     property Prompt: String write SetPrompt;
     property SingleLine: Boolean write FSingleLine;
     property Value: String read GetValue write SetValue;
   end;
 
 function InputQueryMemo(const ACaption, APrompt: String; var AValue: String;
-  const ASingleLine: Boolean = False; const ADocImageClick: TNotifyEvent = nil): Boolean;
+  const ASingleLine: Boolean = False; const ADocBitBtnClick: TNotifyEvent = nil): Boolean;
 
 implementation
 
@@ -56,14 +56,14 @@ uses
 {$R *.DFM}
 
 function InputQueryMemo(const ACaption, APrompt: String; var AValue: String;
-  const ASingleLine: Boolean; const ADocImageClick: TNotifyEvent): Boolean;
+  const ASingleLine: Boolean; const ADocBitBtnClick: TNotifyEvent): Boolean;
 begin
   with TInputQueryMemoForm.Create(Application) do try
     Caption := ACaption;
     Prompt := APrompt;
     Value := AValue;
     SingleLine := ASingleLine;
-    DocImageClick := ADocImageClick;
+    DocBitBtnClick := ADocBitBtnClick;
     if ShowModal = mrOk then begin
       AValue := Value;
       Result := True;
@@ -92,10 +92,10 @@ begin
   Result := ValueControl.Text;
 end;
 
-procedure TInputQueryMemoForm.SetDocImageClick(const Value: TNotifyEvent);
+procedure TInputQueryMemoForm.SetDocBitBtnClick(const Value: TNotifyEvent);
 begin
-  DocImage.OnClick := Value;
-  DocImage.Visible := Assigned(DocImage.OnClick);
+  DocBitBtn.OnClick := Value;
+  DocBitBtn.Visible := Assigned(DocBitBtn.OnClick);
 end;
 
 procedure TInputQueryMemoForm.SetPrompt(const APrompt: String);
@@ -149,7 +149,7 @@ procedure TInputQueryMemoForm.UpdateImages;
 begin
  { After a DPI change the button's Width and Height isn't yet updated, so calculate it ourselves }
   var WH := MulDiv(16, CurrentPPI, 96);
-  DocImage.Picture.Graphic:= GetImage(MainForm.HelpButton, WH);
+  DocBitBtn.Bitmap.Assign(GetImage(MainForm.HelpButton, WH));
 end;
 
 end.

+ 23 - 18
Projects/Src/IDE.MainForm.dfm

@@ -373,33 +373,38 @@ object MainForm: TMainForm
     TabOrder = 1
     Visible = False
     StyleName = 'Windows'
-    object UpdatePanelClosePaintBox: TPaintBox
+    object UpdatePanelCloseBitBtn: TBitmapButton
       AlignWithMargins = True
-      Left = 330
-      Top = 10
-      Width = 21
-      Height = 21
-      Margins.Top = 10
-      Margins.Right = 10
-      Margins.Bottom = 10
+      Left = 328
+      Top = 8
+      Width = 25
+      Height = 25
+      Margins.Top = 8
+      Margins.Right = 8
+      Margins.Bottom = 8
       Align = alRight
-      OnClick = UpdatePanelClosePaintBoxClick
-      OnPaint = UpdatePanelClosePaintBoxPaint
+      Caption = 'Close Banner'
+      TabOrder = 1
+      OnClick = UpdatePanelCloseBitBtnClick
+      OnPaint = UpdatePanelCloseBitBtnPaint
     end
-    object UpdatePanelDonateImage: TImage
+    object UpdatePanelDonateBitBtn: TBitmapButton
       AlignWithMargins = True
-      Left = 303
-      Top = 10
-      Width = 21
-      Height = 21
+      Left = 300
+      Top = 8
+      Width = 25
+      Height = 25
       Cursor = crHandPoint
-      Margins.Top = 10
-      Margins.Bottom = 10
+      Margins.Top = 8
+      Margins.Right = 0
+      Margins.Bottom = 8
       Align = alRight
+      Caption = 'Donate'
       Center = True
       ParentShowHint = False
       ShowHint = True
-      OnClick = UpdatePanelDonateImageClick
+      TabOrder = 2
+      OnClick = UpdatePanelDonateBitBtnClick
     end
     object UpdateLinkLabel: TLinkLabel
       Left = 13

+ 21 - 20
Projects/Src/IDE.MainForm.pas

@@ -26,7 +26,7 @@ uses
   Generics.Collections, UIStateForm, StdCtrls, ExtCtrls, Menus, Buttons, ComCtrls, CommCtrl,
   ScintInt, ScintEdit, IDE.ScintStylerInnoSetup, NewTabSet, ModernColors, IDE.IDEScintEdit,
   Shared.DebugStruct, Shared.CompilerInt.Struct, NewUxTheme, ImageList, ImgList, ToolWin, IDE.HelperFunc,
-  VirtualImageList, BaseImageCollection;
+  VirtualImageList, BaseImageCollection, BitmapButton;
 
 const
   WM_StartCommandLineCompile = WM_USER + $1000;
@@ -263,8 +263,8 @@ type
     EFindRegEx: TMenuItem;
     UpdatePanel: TPanel;
     UpdateLinkLabel: TLinkLabel;
-    UpdatePanelClosePaintBox: TPaintBox;
-    UpdatePanelDonateImage: TImage;
+    UpdatePanelCloseBitBtn: TBitmapButton;
+    UpdatePanelDonateBitBtn: TBitmapButton;
     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
     procedure FExitClick(Sender: TObject);
     procedure FOpenMainFileClick(Sender: TObject);
@@ -389,9 +389,9 @@ type
     procedure EFindRegExClick(Sender: TObject);
     procedure UpdateLinkLabelLinkClick(Sender: TObject; const Link: string;
       LinkType: TSysLinkType);
-    procedure UpdatePanelClosePaintBoxPaint(Sender: TObject);
-    procedure UpdatePanelClosePaintBoxClick(Sender: TObject);
-    procedure UpdatePanelDonateImageClick(Sender: TObject);
+    procedure UpdatePanelCloseBitBtnPaint(Sender: TObject; Canvas: TCanvas; var ARect: TRect);
+    procedure UpdatePanelCloseBitBtnClick(Sender: TObject);
+    procedure UpdatePanelDonateBitBtnClick(Sender: TObject);
   private
     { Private declarations }
     FMemos: TList<TIDEScintEdit>;                      { FMemos[0] is the main memo and FMemos[1] the preprocessor output memo - also see MemosTabSet comment above }
@@ -1010,7 +1010,7 @@ begin
 
   ToolBarPanel.ParentBackground := False;
   UpdatePanel.ParentBackground := False;
-  UpdatePanelDonateImage.Hint := RemoveAccelChar(HDonate.Caption);
+  UpdatePanelDonateBitBtn.Hint := RemoveAccelChar(HDonate.Caption);
 
   UpdateImages;
 
@@ -1028,6 +1028,7 @@ begin
   FHiddenFiles := TStringList.Create(dupError, True, True);
   FActiveMemo := FMainMemo;
   FActiveMemo.Visible := True;
+  ActiveControl := FActiveMemo;
   FErrorMemo := FMainMemo;
   FStepMemo := FMainMemo;
   UpdateMarginsAndSquigglyAndCaretWidths;
@@ -1321,8 +1322,12 @@ begin
       if ControlToAdd <> nil then
         AddControlToArray(ControlToAdd, Controls, NControls);
     end;
-    if UpdatePanel.Visible and FUpdatePanelMessages[UpdateLinkLabel.Tag].HasLink then
-      AddControlToArray(UpdateLinkLabel, Controls, NControls);
+    if UpdatePanel.Visible then begin
+      if FUpdatePanelMessages[UpdateLinkLabel.Tag].HasLink then
+        AddControlToArray(UpdateLinkLabel, Controls, NControls);
+      AddControlToArray(UpdatePanelDonateBitBtn, Controls, NControls);
+      AddControlToArray(UpdatePanelCloseBitBtn, Controls, NControls);
+    end;
 
     { Now move focus to next }
     if NControls > 1 then begin
@@ -1666,9 +1671,6 @@ begin
     to the form's height decreasing }
   if StatusPanel.Visible then
     UpdateStatusPanelHeight(StatusPanel.Height);
-  { Violently resizing the form leaves UpdatePanelDonateImage and UpdatePanelPaintBox artifacts
-    under and over UpdateLinkLabel. Invalidating it prevents this. }
-  UpdateLinkLabel.Invalidate;
 end;
 
 procedure TMainForm.WndProc(var Message: TMessage);
@@ -4179,7 +4181,7 @@ begin
   var Images := ImagesModule.LightToolBarImageCollection;
 
   var Image := Images.GetSourceImage(Images.GetIndexByName('heart-filled'), WH, WH);
-  UpdatePanelDonateImage.Picture.Graphic:= Image;
+  UpdatePanelDonateBitBtn.Bitmap.Assign(Image);
 end;
 
 procedure TMainForm.UpdateOutputTabSetListsItemHeightAndDebugTimeWidth;
@@ -8021,10 +8023,10 @@ begin
   end else
     Handled := False;
   if Handled then
-    UpdatePanelClosePaintBoxClick(Sender);
+    UpdatePanelCloseBitBtnClick(Sender);
 end;
 
-procedure TMainForm.UpdatePanelClosePaintBoxClick(Sender: TObject);
+procedure TMainForm.UpdatePanelCloseBitBtnClick(Sender: TObject);
 begin
   var MessageToHideIndex := UpdateLinkLabel.Tag;
   var Ini := TConfigIniFile.Create;
@@ -8037,20 +8039,19 @@ begin
   UpdateUpdatePanel;
 end;
 
-procedure TMainForm.UpdatePanelDonateImageClick(Sender: TObject);
+procedure TMainForm.UpdatePanelDonateBitBtnClick(Sender: TObject);
 begin
   HDonate.Click;
 end;
 
-procedure TMainForm.UpdatePanelClosePaintBoxPaint(Sender: TObject);
+procedure TMainForm.UpdatePanelCloseBitBtnPaint(Sender: TObject; Canvas: TCanvas; var ARect: TRect);
 const
   MENU_SYSTEMCLOSE = 17;
   MSYSC_NORMAL = 1;
 begin
-  var Canvas := UpdatePanelClosePaintBox.Canvas;
-  var R := TRect.Create(0, 0, UpdatePanelClosePaintBox.Width, UpdatePanelClosePaintBox.Height);
+  var R := ARect;
   if FMenuThemeData <> 0 then begin
-    var Offset := MulDiv(1, CurrentPPI, 96);
+    var Offset := MulDiv(2, CurrentPPI, 96);
     Inc(R.Left, Offset);
     DrawThemeBackground(FMenuThemeData, Canvas.Handle, MENU_SYSTEMCLOSE, MSYSC_NORMAL, R, nil);
   end else begin

+ 8 - 16
Projects/Src/IDE.RegistryDesignerForm.dfm

@@ -18,15 +18,14 @@ object RegistryDesignerForm: TRegistryDesignerForm
     496
     347)
   TextHeight = 13
-  object AppRegistryMinVerDocImage: TImage
-    Left = 406
-    Top = 170
-    Width = 16
-    Height = 16
+  object AppRegistryMinVerDocBitBtn: TBitmapButton
+    Left = 404
+    Top = 168
+    Width = 20
+    Height = 20
     Anchors = [akTop, akRight]
-    AutoSize = True
-    Transparent = True
-    ExplicitLeft = 414
+    Caption = 'Help'
+    TabOrder = 9
   end
   object Panel1: TPanel
     Left = 0
@@ -35,9 +34,7 @@ object RegistryDesignerForm: TRegistryDesignerForm
     Height = 42
     Align = alBottom
     BevelOuter = bvNone
-    TabOrder = 9
-    ExplicitTop = 320
-    ExplicitWidth = 500
+    TabOrder = 10
     DesignSize = (
       496
       42)
@@ -61,7 +58,6 @@ object RegistryDesignerForm: TRegistryDesignerForm
       ModalResult = 1
       TabOrder = 0
       OnClick = InsertButtonClick
-      ExplicitLeft = 330
     end
     object CancelButton: TButton
       Left = 406
@@ -73,7 +69,6 @@ object RegistryDesignerForm: TRegistryDesignerForm
       Caption = 'Cancel'
       ModalResult = 2
       TabOrder = 1
-      ExplicitLeft = 410
     end
     object PrivilegesRequiredLabel: TNewStaticText
       Left = 8
@@ -101,7 +96,6 @@ object RegistryDesignerForm: TRegistryDesignerForm
     Height = 21
     Anchors = [akLeft, akTop, akRight]
     TabOrder = 1
-    ExplicitWidth = 392
   end
   object AppRegistryFileButton: TButton
     Left = 406
@@ -111,7 +105,6 @@ object RegistryDesignerForm: TRegistryDesignerForm
     Anchors = [akTop, akRight]
     Caption = '&Browse...'
     TabOrder = 2
-    ExplicitLeft = 414
   end
   object AppRegistrySettingsLabel: TNewStaticText
     Left = 8
@@ -165,6 +158,5 @@ object RegistryDesignerForm: TRegistryDesignerForm
     Height = 21
     Anchors = [akLeft, akTop, akRight]
     TabOrder = 8
-    ExplicitWidth = 140
   end
 end

+ 4 - 4
Projects/Src/IDE.RegistryDesignerForm.pas

@@ -2,7 +2,7 @@ unit IDE.RegistryDesignerForm;
 
 {
   Inno Setup
-  Copyright (C) 1997-2024 Jordan Russell
+  Copyright (C) 1997-2025 Jordan Russell
   Portions by Martijn Laan
   For conditions of distribution and use, see LICENSE.TXT.
 
@@ -16,7 +16,7 @@ interface
 uses
   SysUtils, Classes,
   Forms, Controls, StdCtrls, ExtCtrls,
-  IDE.Wizard.WizardFormRegistryHelper, NewStaticText;
+  IDE.Wizard.WizardFormRegistryHelper, NewStaticText, BitmapButton;
 
 type
   TRegistryDesignerForm = class(TForm)
@@ -34,7 +34,7 @@ type
     AppRegistryMinVerCheck: TCheckBox;
     AppRegistryMinVerEdit: TEdit;
     PrivilegesRequiredLabel: TNewStaticText;
-    AppRegistryMinVerDocImage: TImage;
+    AppRegistryMinVerDocBitBtn: TBitmapButton;
     procedure InsertButtonClick(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
@@ -74,7 +74,7 @@ begin
   FRegistryHelper := TWizardFormRegistryHelper.Create(Self, AppRegistryFileEdit,
     AppRegistryFileButton, AppRegistryUninsDeleteKeyCheck,
     AppRegistryUninsDeleteKeyIfEmptyCheck, AppRegistryUninsDeleteValueCheck,
-    AppRegistryMinVerCheck, AppRegistryMinVerEdit, AppRegistryMinVerDocImage);
+    AppRegistryMinVerCheck, AppRegistryMinVerEdit, AppRegistryMinVerDocBitBtn);
 end;
 
 procedure TRegistryDesignerForm.FormDestroy(Sender: TObject);

+ 3 - 3
Projects/Src/IDE.SignToolsForm.pas

@@ -32,7 +32,7 @@ type
     procedure SignToolsListBoxDblClick(Sender: TObject);
   private
     FSignTools: TStringList;
-    procedure CommandDocImageClick(Sender: TObject);
+    procedure CommandDocBitBtnClick(Sender: TObject);
     procedure UpdateSignTools;
     procedure UpdateSignToolsButtons;
     procedure SetSignTools(SignTools: TStringList);
@@ -91,7 +91,7 @@ begin
   SendMessage(Handle, WM_SETICON, ICON_BIG, 0);
 end;
 
-procedure TSignToolsForm.CommandDocImageClick(Sender: TObject);
+procedure TSignToolsForm.CommandDocBitBtnClick(Sender: TObject);
 begin
   if Assigned(HtmlHelp) then
     HtmlHelp(GetDesktopWindow, PChar(GetHelpFile), HH_DISPLAY_TOPIC, Cardinal(PChar('topic_setup_signtool.htm')));
@@ -128,7 +128,7 @@ begin
       end;
     end;
 
-    if InputQueryMemo(Caption, 'Command of the Sign Tool:', SignToolCommand, True, CommandDocImageClick) then begin
+    if InputQueryMemo(Caption, 'Command of the Sign Tool:', SignToolCommand, True, CommandDocBitBtnClick) then begin
       if SignToolCommand = '' then begin
         AppMessageBox(PChar('Invalid command.'), PChar(Caption), MB_OK or MB_ICONSTOP);
         Exit;

+ 170 - 172
Projects/Src/IDE.StartupForm.dfm

@@ -18,30 +18,28 @@ object StartupForm: TStartupForm
     579
     419)
   TextHeight = 13
-  object DonateImage: TImage
-    Left = 8
-    Top = 383
-    Width = 62
-    Height = 31
+  object DonateBitBtn: TBitmapButton
+    Left = 6
+    Top = 381
+    Width = 66
+    Height = 35
     Cursor = crHandPoint
-    Hint = 'Support Inno Setup - Thank you!'
     Anchors = [akLeft, akBottom]
-    AutoSize = True
+    Caption = 'Donate'
     ParentShowHint = False
-    Picture.Data = {
-      07544269746D6170F60B0000424DF60B00000000000036040000280000003E00
-      00001F0000000100080000000000C0070000C40E0000C40E0000000100000001
-      000000000000663300006F3F0F004D36220070401000794C1D0073482400794D
-      200082582C0081573200835930008C643B00996633009F6F3E008C664000926E
-      4E009F70400095704900967350009E7D5800A5784A00A6794D00AC815500AC83
-      5900A0806000A8896700A2836800B28A6100B38C6600B8936C00A98D7000B195
-      7500B2997F00BE9D7800BAA18300BFA08000B8A08800BCA58F00BDA79400BBBB
-      BB00C4A68300C6A98D00CAAE8E00C4AD9200C5B29F00CCB29900D1B79A00CAB7
-      A400CDBAA100CFBFAF00D2BCA500D7C0A500D6C6AF00D3C4B400D9C5B200DDCA
-      B100D9CCBF00E0D2BE00E3D3BC00DAD5D100E2D9CF00E5D9CC00E9DCC800E9DE
-      CD00E5DCD200E9E1D700ECE5DF00F0E5D300F3EBDB00F6EEDF00E9E9E900F2EC
-      E500F4F1EE00F5F2EF00FCF7EA00F9F5F200FFFFFF0000000000000000000000
-      0000000000000000000000000000000000000000000000000000000000000000
+    Bitmap.Data = {
+      F60B0000424DF60B00000000000036040000280000003E0000001F0000000100
+      080000000000C0070000C40E0000C40E00000001000000010000000000006633
+      00006F3F0F004D36220070401000794C1D0073482400794D200082582C008157
+      3200835930008C643B00996633009F6F3E008C664000926E4E009F7040009570
+      4900967350009E7D5800A5784A00A6794D00AC815500AC835900A0806000A889
+      6700A2836800B28A6100B38C6600B8936C00A98D7000B1957500B2997F00BE9D
+      7800BAA18300BFA08000B8A08800BCA58F00BDA79400BBBBBB00C4A68300C6A9
+      8D00CAAE8E00C4AD9200C5B29F00CCB29900D1B79A00CAB7A400CDBAA100CFBF
+      AF00D2BCA500D7C0A500D6C6AF00D3C4B400D9C5B200DDCAB100D9CCBF00E0D2
+      BE00E3D3BC00DAD5D100E2D9CF00E5D9CC00E9DCC800E9DECD00E5DCD200E9E1
+      D700ECE5DF00F0E5D300F3EBDB00F6EEDF00E9E9E900F2ECE500F4F1EE00F5F2
+      EF00FCF7EA00F9F5F200FFFFFF00000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
@@ -63,99 +61,99 @@ object StartupForm: TStartupForm
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      0000060603030303030303030303030303030303030303030303030303030303
+      0000000000000000000000000000000000000000000000000000060603030303
       0303030303030303030303030303030303030303030303030303030303030303
-      0000094135272727272727272727272727272727272727272727272727272727
-      2727272727272727272727272727272727272727272727272727272727272603
-      00000F4C48464646464646464646464646464646464646464646464646464646
-      4646464646464646464646464646464646464646464646464646464646413506
-      00001A4C4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A
-      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4C4C06
-      00001A4C4C4C4C4C4C4C4C4C4C4C4C000000004C4C4C0000004C4C004C4C4C00
-      4C4C004C4C4C004C4C4C004C4C4C000000004C4C4C4C4C4C4C4C4C4C4C483B06
-      00001A4C4A4A4A4A4A4A4A4A4A4A4A004A4A4A004A004A4A4A004A004A4A0000
-      4A4A004A4A4A004A4A4A004A4A4A004A4A4A4A4A4A4A4A4A4A4A4A4A4A483B06
-      00001A4C4C4C4C4C4C4C4C4C4C4C4C004C4C4C004C004C4C4C004C004C004C00
-      4C4C00000000004C4C4C004C4C4C0000004C4C4C4C4C4C4C4C4C4C4C4C483B06
-      00001A4C4A4A4A4A4A4A4A4A4A4A4A004A4A4A004A004A4A4A004A00004A4A00
-      4A4A004A4A4A004A4A4A004A4A4A004A4A4A4A4A4A4A4A4A4A4A4A4A4A483B06
-      00001A4C4C4C4C4C4C4C4C4C4C4C4C000000004C4C4C0000004C4C004C4C4C00
-      4C4C4C0000004C4C00000000004C000000004C4C4C4C4C4C4C4C4C4C4C483B06
-      00001A4C4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A
-      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A483B06
-      00001A4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C
-      4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C483B06
-      00001A4C4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A
-      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A483B06
-      00001A4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C312025494C4C
-      4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C483B06
-      00001A4C4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A1F0101114A4A
-      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A483B06
-      00001A4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C070101384C
-      4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C483B06
-      00001A4C4A4A4A4A4A4A4A4A4A4A4A4A4A4A3F34394A4A4A4A4A4A300101054A
-      4A4A4A4A4A4A4A4A4A4A4A4A4A3E3A454A4A4A4A4A4A4A4A4A4A4A4A4A483B06
-      00001A4C4C4C4C4C01010E4C4C4C4C4C4C2C0101010E01010A4C4C3801010120
-      4C4C360C0C2D4C4C4C4C4C4C230C0C0C1C0C0C293D0C0C324C4C4C4C4C483B06
-      00001A4C4A4A4A4A020101444A4A4A4A4A02010108080101013F4A1F01010102
-      3F4A3A0C0C164A4A4A4A4A3E0C0C0C1B140C0C16430C0C1D4A4A4A4A4A483B06
-      00001A4C4C4C4C4C0E0101314C4C4C4C4C070101494C1E0101314C1801010101
-      124C4C0C0C0C4B4C4C4C4C470C0C174C4C170C0C4B100C104C4C4C4C4C483B06
-      00001A4C4A4A4A4A1F0101050B111F394A2201010519130101194A0B01010501
-      01344A1B0C0C161B282E434A1B0C0C162A1B0C0C371D0C0C434A4A4A4A483B06
-      00001A4C4C4C4C4C38010101010101010E49310E01010101010A4C0401043801
-      01074C2D0C0C0C0C0C0C0C294B2D150C0C0C0C0C292D0C0C364C4C4C4C483B06
-      00001A4C4A4A4A4A4A01010119190201010844394A3F340501014401010B4A11
-      01011F3A0C0C0D2A210C0C0C2843434A3A370C0C1B3A0C0C214A4A4A4A483B06
-      00001A4C4C4C4C4C4C0E0101384C490401012001040E070101013801011E4C42
-      040104400C0C0C4C4C3D0C0C0C2D0C171C100C0C1C4C0C0C174C4C4C4C483B06
-      00001A4C4A4A4A4A4A1901011F4A4A0801012B05010101010222340101224A4A
-      1F010111160C0C3E4A450C0C0C3E0C0C0C0C0C143A4A160C0C454A4A4A483B06
-      00001A4C4C4C4C4C4C310101040E0A010101384C4C3C38424C4C4C38384C4C4C
-      4C3C3838290C0C151C150C0C0C4C4C4B3D3D474C4C4C290C0C3D4C4C4C483B06
-      00001A4C4A4A4A4A4A4401010101010101134A4A4A4A4A4A4A4A4A4A4A4A4A4A
-      4A4A4A4A370C0C0C0C0C0C0C334A4A4A4A4A4A4A4A4A370C0C2A3A4A4A483B06
-      00001A4C4C4C4C4C4C4C3120202020253C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C
-      4C4C4C4C4B2D2D2D2D2D36474C4C4C4C4C4C4C4C4C4C4C322D323D4C4C483B06
-      00001A4C4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A
-      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4C4606
-      0000244C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C
-      4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C06
-      0000244A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A
-      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A09
-      0000262F26262626262626262626262626262626262626262626262626262626
-      2626262626262626262626262626262626262626262626262626262626241A0F
-      0000}
+      0303030303030303030303030303030303030303030303030000094135272727
+      2727272727272727272727272727272727272727272727272727272727272727
+      27272727272727272727272727272727272727272727260300000F4C48464646
+      4646464646464646464646464646464646464646464646464646464646464646
+      46464646464646464646464646464646464646464641350600001A4C4A4A4A4A
+      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A
+      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4C4C0600001A4C4C4C4C4C
+      4C4C4C4C4C4C4C000000004C4C4C0000004C4C004C4C4C004C4C004C4C4C004C
+      4C4C004C4C4C000000004C4C4C4C4C4C4C4C4C4C4C483B0600001A4C4A4A4A4A
+      4A4A4A4A4A4A4A004A4A4A004A004A4A4A004A004A4A00004A4A004A4A4A004A
+      4A4A004A4A4A004A4A4A4A4A4A4A4A4A4A4A4A4A4A483B0600001A4C4C4C4C4C
+      4C4C4C4C4C4C4C004C4C4C004C004C4C4C004C004C004C004C4C00000000004C
+      4C4C004C4C4C0000004C4C4C4C4C4C4C4C4C4C4C4C483B0600001A4C4A4A4A4A
+      4A4A4A4A4A4A4A004A4A4A004A004A4A4A004A00004A4A004A4A004A4A4A004A
+      4A4A004A4A4A004A4A4A4A4A4A4A4A4A4A4A4A4A4A483B0600001A4C4C4C4C4C
+      4C4C4C4C4C4C4C000000004C4C4C0000004C4C004C4C4C004C4C4C0000004C4C
+      00000000004C000000004C4C4C4C4C4C4C4C4C4C4C483B0600001A4C4A4A4A4A
+      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A
+      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A483B0600001A4C4C4C4C4C
+      4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C
+      4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C483B0600001A4C4A4A4A4A
+      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A
+      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A483B0600001A4C4C4C4C4C
+      4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C312025494C4C4C4C4C4C4C4C4C4C
+      4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C483B0600001A4C4A4A4A4A
+      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A1F0101114A4A4A4A4A4A4A4A4A4A
+      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A483B0600001A4C4C4C4C4C
+      4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C070101384C4C4C4C4C4C4C4C4C
+      4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C483B0600001A4C4A4A4A4A
+      4A4A4A4A4A4A4A4A4A4A3F34394A4A4A4A4A4A300101054A4A4A4A4A4A4A4A4A
+      4A4A4A4A4A3E3A454A4A4A4A4A4A4A4A4A4A4A4A4A483B0600001A4C4C4C4C4C
+      01010E4C4C4C4C4C4C2C0101010E01010A4C4C38010101204C4C360C0C2D4C4C
+      4C4C4C4C230C0C0C1C0C0C293D0C0C324C4C4C4C4C483B0600001A4C4A4A4A4A
+      020101444A4A4A4A4A02010108080101013F4A1F010101023F4A3A0C0C164A4A
+      4A4A4A3E0C0C0C1B140C0C16430C0C1D4A4A4A4A4A483B0600001A4C4C4C4C4C
+      0E0101314C4C4C4C4C070101494C1E0101314C1801010101124C4C0C0C0C4B4C
+      4C4C4C470C0C174C4C170C0C4B100C104C4C4C4C4C483B0600001A4C4A4A4A4A
+      1F0101050B111F394A2201010519130101194A0B0101050101344A1B0C0C161B
+      282E434A1B0C0C162A1B0C0C371D0C0C434A4A4A4A483B0600001A4C4C4C4C4C
+      38010101010101010E49310E01010101010A4C040104380101074C2D0C0C0C0C
+      0C0C0C294B2D150C0C0C0C0C292D0C0C364C4C4C4C483B0600001A4C4A4A4A4A
+      4A01010119190201010844394A3F340501014401010B4A1101011F3A0C0C0D2A
+      210C0C0C2843434A3A370C0C1B3A0C0C214A4A4A4A483B0600001A4C4C4C4C4C
+      4C0E0101384C490401012001040E070101013801011E4C42040104400C0C0C4C
+      4C3D0C0C0C2D0C171C100C0C1C4C0C0C174C4C4C4C483B0600001A4C4A4A4A4A
+      4A1901011F4A4A0801012B05010101010222340101224A4A1F010111160C0C3E
+      4A450C0C0C3E0C0C0C0C0C143A4A160C0C454A4A4A483B0600001A4C4C4C4C4C
+      4C310101040E0A010101384C4C3C38424C4C4C38384C4C4C4C3C3838290C0C15
+      1C150C0C0C4C4C4B3D3D474C4C4C290C0C3D4C4C4C483B0600001A4C4A4A4A4A
+      4A4401010101010101134A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A370C0C0C
+      0C0C0C0C334A4A4A4A4A4A4A4A4A370C0C2A3A4A4A483B0600001A4C4C4C4C4C
+      4C4C3120202020253C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4B2D2D2D
+      2D2D36474C4C4C4C4C4C4C4C4C4C4C322D323D4C4C483B0600001A4C4A4A4A4A
+      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A
+      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4C46060000244C4C4C4C4C
+      4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C
+      4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C4C060000244A4A4A4A4A
+      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A
+      4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A090000262F26262626
+      2626262626262626262626262626262626262626262626262626262626262626
+      262626262626262626262626262626262626262626241A0F0000}
     ShowHint = True
-    OnClick = DonateImageClick
+    TabOrder = 4
+    OnClick = DonateBitBtnClick
   end
-  object MailingListImage: TImage
-    Left = 78
-    Top = 383
-    Width = 62
-    Height = 31
+  object MailingListBitBtn: TBitmapButton
+    Left = 76
+    Top = 381
+    Width = 66
+    Height = 35
     Cursor = crHandPoint
     Hint = 'Be notified by e-mail of new Inno Setup releases'
     Anchors = [akLeft, akBottom]
-    AutoSize = True
+    Caption = 'Subscribe'
     ParentShowHint = False
-    Picture.Data = {
-      07544269746D6170F60B0000424DF60B00000000000036040000280000003E00
-      00001F0000000100080000000000C0070000C40E0000C40E0000000100000001
-      0000000000005D2E03005F2F0200512C0A00532C0900542D0800602F0100612F
-      0100622F0000632F000063330200643000006432010066320000673300006936
-      03006A3603006E3A0700663B0F006B3A09006D3A08006F3B08006E3C0A006F3D
-      0C00703C0800713D0B00713F0D00723E0C00733F0C004D36220074400D007541
-      0F007A461300734824007F5B37008E5B2A008F5B29008F5D2B008E5E2E008F5E
-      2D00905E2C00915E2C0081573200875D3200805B3600815B3600845C34008C5E
-      300093602E008D633A0091643700926436009364360095653500966534009765
-      34009865330090643800926E4E00B3815000B4815000B7855300B4855400B588
-      5B00BA8B5B00A2836800D19F6E00DBAD7E00DDAD7C00B8A08800BDA79400BBBB
-      BB00E1B58A00E3B68900CAB7A400D3BBA200D4BBA100D7C1AA00DBC3AA00D3C4
-      B400DFCFBB00DAD5D100E0D4C300E6DFCF00E9E0CF00E8E1D100E9E1D100E9E1
-      D700E9E9E900F2F0E300F1F0E400FCF7E500FCF8E200FCF8E400F4F1EE00F5F5
-      ED00F6F6EF00FCF7E900F8F8F200F9F9F200FCFCF600FDFDF600FEFEF900FFFF
-      FA00FFFFFE000000000000000000000000000000000000000000000000000000
+    Bitmap.Data = {
+      F60B0000424DF60B00000000000036040000280000003E0000001F0000000100
+      080000000000C0070000C40E0000C40E00000001000000010000000000005D2E
+      03005F2F0200512C0A00532C0900542D0800602F0100612F0100622F0000632F
+      00006333020064300000643201006632000067330000693603006A3603006E3A
+      0700663B0F006B3A09006D3A08006F3B08006E3C0A006F3D0C00703C0800713D
+      0B00713F0D00723E0C00733F0C004D36220074400D0075410F007A4613007348
+      24007F5B37008E5B2A008F5B29008F5D2B008E5E2E008F5E2D00905E2C00915E
+      2C0081573200875D3200805B3600815B3600845C34008C5E300093602E008D63
+      3A00916437009264360093643600956535009665340097653400986533009064
+      3800926E4E00B3815000B4815000B7855300B4855400B5885B00BA8B5B00A283
+      6800D19F6E00DBAD7E00DDAD7C00B8A08800BDA79400BBBBBB00E1B58A00E3B6
+      8900CAB7A400D3BBA200D4BBA100D7C1AA00DBC3AA00D3C4B400DFCFBB00DAD5
+      D100E0D4C300E6DFCF00E9E0CF00E8E1D100E9E1D100E9E1D700E9E9E900F2F0
+      E300F1F0E400FCF7E500FCF8E200FCF8E400F4F1EE00F5F5ED00F6F6EF00FCF7
+      E900F8F8F200F9F9F200FCFCF600FDFDF600FEFEF900FFFFFA00FFFFFE000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
@@ -174,71 +172,72 @@ object StartupForm: TStartupForm
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
       0000000000000000000000000000000000000000000000000000000000000000
-      000021211D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D
+      000000000000000000000000000000000000000000000000000021211D1D1D1D
       1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D
-      00002A574F474747474747474747474747474747474747474747474747474747
-      474747474747474747474747474747474747474747474747474747474747461D
-      00003A685E585858585858585858585858585858585858585858585858585858
-      5858585858585858585858585858585858585858585858585858585858574F21
-      0000416861616161616161616161616161616161616161616161616161616161
-      6161616161616161616161616161616161616161616161616161616161686821
-      0000416868680000000068686800000068680000000068680000000068686800
-      00006868006868680068000000000068000000006868000000000068685E5121
-      0000416861616161616100610061616100610061616100616161616100610061
-      61610061006161610061616100616161006161610061006161616161615E5121
-      0000416868686800000068680068686800680000000068686800000068680068
-      68686868000000006868686800686868000000006868000000006868685E5121
-      0000416861610061616161610061616100610061616100610061616161610061
-      61610061006161610061616100616161006161610061006161616161615E5121
-      0000416868686800000000680068686800680000000068686800000000686800
-      00006868000000006868000000000068000000006868000000000068685E5121
-      0000416861616161616161616161616161616161616161616161616161616161
-      61616161616161616161616161616161616161616161616161616161615E5121
-      0000416868686868686868686868686868686868686868686868686868686868
-      68686868686868686868686868686868686868686868686868686868685E5121
-      0000416861616161616161616161616161616161616161616161616161616161
-      61616161616161616161616161616161616161616161616161616161615E5121
-      0000416868686868686868686868686868686868686868686867676767676767
-      67676767676767676768686868686868686868686868686868686868685E5121
-      00004168616161616161616161616161616161616161615D1A09090909080809
-      09090909090909091C5C616161616161616161616161616161616161615E5121
-      0000416868686868686868686868686868686868686868121437363331313235
-      3838383838383838181C676868686868686868686868686868686868685E5121
-      0000416861616161616161616161616161616161616161052B67686868686867
-      676767676767676729095C6161616161616161616161616161616161615E5121
-      0000416868686868686868686868686868686868686868042D68686868685F4D
-      424D6068686868672909676868686868686868686868686868686868685E5121
-      000041686161616161616161616161616161616161616105226868686752240B
-      0B0925526768686729095D6161616161616161616161616161616161615E5121
-      0000416868686868686868686868686868686868686868052C68686448130F3D
-      4E3E1014496468672909676868686868686868686868686868686868685E5121
-      0000416861616161616161616161616161616161616161032E68633F0A174B66
-      68664C190E40626727075D6161616161616161616161616161616161615E5121
-      00004168686868686868686868686868686868686868680326532F0723556868
-      68686856240930542907676868686868686868686868686868686868685E5121
-      0000416861616161616161616161616161616161616161011F200C3B5A686868
-      68686868593C0D201F095D6161616161616161616161616161616161615E5121
-      0000416868686868686868686868686868686868686868080B15436568686868
-      68686868686544150909676868686868686868686868686868686868685E5121
-      000041686161616161616161616161616161616161615B081E50666868686868
-      68686868686866501B095D6161616161616161616161616161616161615E5121
-      0000416868686868686868686868686868686868686868072867686868686868
-      68686868686867672909676868686868686868686868686868686868685E5121
-      000041686161616161616161616161616161616161615B161135393932323334
-      3333333436373838111B5D6161616161616161616161616161616161615E5121
-      0000416868686868686868686868686868686868686868671C08060602060606
-      070707070909090B1E67686868686868686868686868686868686868685E5121
-      0000416861616161616161616161616161616161616161615B5B5B5D5D5B5B5B
-      5B5B5B5B5B5B5D5D5D6161616161616161616161616161616161616161685821
-      0000456868686868686868686868686868686868686868686868686868686868
-      6868686868686868686868686868686868686868686868686868686868686821
-      0000456161616161616161616161616161616161616161616161616161616161
-      616161616161616161616161616161616161616161616161616161616161612A
-      0000464A46464646464646464646464646464646464646464646464646464646
-      464646464646464646464646464646464646464646464646464646464645413A
-      0000}
+      1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D00002A574F474747
+      4747474747474747474747474747474747474747474747474747474747474747
+      47474747474747474747474747474747474747474747461D00003A685E585858
+      5858585858585858585858585858585858585858585858585858585858585858
+      585858585858585858585858585858585858585858574F210000416861616161
+      6161616161616161616161616161616161616161616161616161616161616161
+      6161616161616161616161616161616161616161616868210000416868680000
+      0000686868000000686800000000686800000000686868000000686800686868
+      0068000000000068000000006868000000000068685E51210000416861616161
+      6161006100616161006100616161006161616161006100616161006100616161
+      0061616100616161006161610061006161616161615E51210000416868686800
+      0000686800686868006800000000686868000000686800686868686800000000
+      6868686800686868000000006868000000006868685E51210000416861610061
+      6161616100616161006100616161006100616161616100616161006100616161
+      0061616100616161006161610061006161616161615E51210000416868686800
+      0000006800686868006800000000686868000000006868000000686800000000
+      6868000000000068000000006868000000000068685E51210000416861616161
+      6161616161616161616161616161616161616161616161616161616161616161
+      6161616161616161616161616161616161616161615E51210000416868686868
+      6868686868686868686868686868686868686868686868686868686868686868
+      6868686868686868686868686868686868686868685E51210000416861616161
+      6161616161616161616161616161616161616161616161616161616161616161
+      6161616161616161616161616161616161616161615E51210000416868686868
+      6868686868686868686868686868686868676767676767676767676767676767
+      6768686868686868686868686868686868686868685E51210000416861616161
+      6161616161616161616161616161615D1A090909090808090909090909090909
+      1C5C616161616161616161616161616161616161615E51210000416868686868
+      6868686868686868686868686868681214373633313132353838383838383838
+      181C676868686868686868686868686868686868685E51210000416861616161
+      616161616161616161616161616161052B676868686868676767676767676767
+      29095C6161616161616161616161616161616161615E51210000416868686868
+      686868686868686868686868686868042D68686868685F4D424D606868686867
+      2909676868686868686868686868686868686868685E51210000416861616161
+      61616161616161616161616161616105226868686752240B0B09255267686867
+      29095D6161616161616161616161616161616161615E51210000416868686868
+      686868686868686868686868686868052C68686448130F3D4E3E101449646867
+      2909676868686868686868686868686868686868685E51210000416861616161
+      616161616161616161616161616161032E68633F0A174B6668664C190E406267
+      27075D6161616161616161616161616161616161615E51210000416868686868
+      6868686868686868686868686868680326532F07235568686868685624093054
+      2907676868686868686868686868686868686868685E51210000416861616161
+      616161616161616161616161616161011F200C3B5A68686868686868593C0D20
+      1F095D6161616161616161616161616161616161615E51210000416868686868
+      686868686868686868686868686868080B154365686868686868686868654415
+      0909676868686868686868686868686868686868685E51210000416861616161
+      61616161616161616161616161615B081E506668686868686868686868686650
+      1B095D6161616161616161616161616161616161615E51210000416868686868
+      6868686868686868686868686868680728676868686868686868686868686767
+      2909676868686868686868686868686868686868685E51210000416861616161
+      61616161616161616161616161615B1611353939323233343333333436373838
+      111B5D6161616161616161616161616161616161615E51210000416868686868
+      686868686868686868686868686868671C08060602060606070707070909090B
+      1E67686868686868686868686868686868686868685E51210000416861616161
+      616161616161616161616161616161615B5B5B5D5D5B5B5B5B5B5B5B5B5B5D5D
+      5D61616161616161616161616161616161616161616858210000456868686868
+      6868686868686868686868686868686868686868686868686868686868686868
+      6868686868686868686868686868686868686868686868210000456161616161
+      6161616161616161616161616161616161616161616161616161616161616161
+      61616161616161616161616161616161616161616161612A0000464A46464646
+      4646464646464646464646464646464646464646464646464646464646464646
+      46464646464646464646464646464646464646464645413A0000}
     ShowHint = True
-    OnClick = MailingListImageClick
+    TabOrder = 5
+    OnClick = MailingListBitBtnClick
   end
   object OKButton: TButton
     Left = 418
@@ -352,9 +351,8 @@ object StartupForm: TStartupForm
     Top = 390
     Width = 260
     Height = 17
-    TabStop = False
     Anchors = [akLeft, akRight, akBottom]
     Caption = '&Don'#39't show this dialog again'
-    TabOrder = 4
+    TabOrder = 6
   end
 end

+ 9 - 9
Projects/Src/IDE.StartupForm.pas

@@ -2,7 +2,7 @@ unit IDE.StartupForm;
 
 {
   Inno Setup
-  Copyright (C) 1997-2024 Jordan Russell
+  Copyright (C) 1997-2025 Jordan Russell
   Portions by Martijn Laan
   For conditions of distribution and use, see LICENSE.TXT.
 
@@ -13,7 +13,7 @@ interface
 
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
-  UIStateForm, StdCtrls, ExtCtrls;
+  UIStateForm, StdCtrls, ExtCtrls, BitmapButton;
 
 type
   TStartupFormResult = (srNone, srEmpty, srWizard, srOpenFile, srOpenDialog,
@@ -31,8 +31,8 @@ type
     StartupCheck: TCheckBox;
     NewImage: TImage;
     OpenImage: TImage;
-    DonateImage: TImage;
-    MailingListImage: TImage;
+    DonateBitBtn: TBitmapButton;
+    MailingListBitBtn: TBitmapButton;
     procedure RadioButtonClick(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure DblClick_(Sender: TObject);
@@ -40,8 +40,8 @@ type
     procedure OKButtonClick(Sender: TObject);
     procedure FormAfterMonitorDpiChanged(Sender: TObject; OldDPI,
       NewDPI: Integer);
-    procedure DonateImageClick(Sender: TObject);
-    procedure MailingListImageClick(Sender: TObject);
+    procedure DonateBitBtnClick(Sender: TObject);
+    procedure MailingListBitBtnClick(Sender: TObject);
   private
     FResult: TStartupFormResult;
     FResultMainFileName: TFileName;
@@ -101,7 +101,7 @@ begin
   InitFormFont(Self);
   InitFormTheme(Self);
 
-  DonateImage.Hint := MainForm.UpdatePanelDonateImage.Hint;
+  DonateBitBtn.Hint := MainForm.UpdatePanelDonateBitBtn.Hint;
 
   UpdateImages;
 
@@ -152,12 +152,12 @@ begin
   OpenRadioButton.Checked := True;
 end;
 
-procedure TStartupForm.DonateImageClick(Sender: TObject);
+procedure TStartupForm.DonateBitBtnClick(Sender: TObject);
 begin
   OpenDonateSite;
 end;
 
-procedure TStartupForm.MailingListImageClick(Sender: TObject);
+procedure TStartupForm.MailingListBitBtnClick(Sender: TObject);
 begin
   OpenMailingListSite;
 end;

+ 7 - 7
Projects/Src/IDE.Wizard.WizardForm.dfm

@@ -1054,14 +1054,14 @@ object WizardForm: TWizardForm
           DesignSize = (
             485
             245)
-          object AppRegistryMinVerDocImage: TImage
-            Left = 360
-            Top = 161
-            Width = 16
-            Height = 16
+          object AppRegistryMinVerDocBitBtn: TBitmapButton
+            Left = 358
+            Top = 159
+            Width = 20
+            Height = 20
             Anchors = [akTop, akRight]
-            AutoSize = True
-            Transparent = True
+            Caption = 'Help'
+            TabOrder = 9
           end
           object AppRegistryFileLabel: TNewStaticText
             Left = 36

+ 3 - 3
Projects/Src/IDE.Wizard.WizardForm.pas

@@ -14,7 +14,7 @@ interface
 uses
   Windows, Forms, Classes, Graphics, StdCtrls, ExtCtrls, Controls, Dialogs, pngimage,
   UIStateForm, NewStaticText, DropListBox, NewCheckListBox, NewNotebook,
-  IDE.Wizard.WizardFormFilesHelper, IDE.Wizard.WizardFormRegistryHelper;
+  IDE.Wizard.WizardFormFilesHelper, IDE.Wizard.WizardFormRegistryHelper, BitmapButton;
 
 type
   TWizardPage = (wpWelcome, wpAppInfo, wpAppDir, wpAppFiles, wpAppAssoc, wpAppIcons,
@@ -139,7 +139,7 @@ type
     AppRegistryUninsDeleteValueCheck: TCheckBox;
     AppRegistryMinVerCheck: TCheckBox;
     AppRegistryMinVerEdit: TEdit;
-    AppRegistryMinVerDocImage: TImage;
+    AppRegistryMinVerDocBitBtn: TBitmapButton;
     WelcomeImageDark: TImage;
     InnerImageDark: TImage;
     procedure FormCreate(Sender: TObject);
@@ -289,7 +289,7 @@ begin
   FRegistryHelper := TWizardFormRegistryHelper.Create(Self, AppRegistryFileEdit,
     AppRegistryFileButton, AppRegistryUninsDeleteKeyCheck,
     AppRegistryUninsDeleteKeyIfEmptyCheck, AppRegistryUninsDeleteValueCheck,
-    AppRegistryMinVerCheck, AppRegistryMinVerEdit, AppRegistryMinVerDocImage);
+    AppRegistryMinVerCheck, AppRegistryMinVerEdit, AppRegistryMinVerDocBitBtn);
 
   FLanguages := TStringList.Create;
   FLanguages.Sorted := True;

+ 12 - 12
Projects/Src/IDE.Wizard.WizardFormRegistryHelper.pas

@@ -2,7 +2,7 @@ unit IDE.Wizard.WizardFormRegistryHelper;
 
 {
   Inno Setup
-  Copyright (C) 1997-2024 Jordan Russell
+  Copyright (C) 1997-2025 Jordan Russell
   Portions by Martijn Laan
   For conditions of distribution and use, see LICENSE.TXT.
 
@@ -12,7 +12,7 @@ unit IDE.Wizard.WizardFormRegistryHelper;
 interface
 
 uses
-  Forms, StdCtrls, ExtCtrls;
+  Forms, StdCtrls, ExtCtrls, BitmapButton;
 
 type
   TPrivilegesRequired = (prAdmin, prLowest, prDynamic);
@@ -24,7 +24,7 @@ type
       FUninsDeleteKeyCheck, FUninsDeleteKeyIfEmptyCheck,
       FUninsDeleteValueCheck, FMinVerCheck: TCheckBox;
       FMinVerEdit: TEdit;
-      FMinVerDocImage: TImage;
+      FMinVerDocBitBtn: TBitmapButton;
       FPrivilegesRequired: TPrivilegesRequired;
       procedure SetPrivilegesRequired(const Value: TPrivilegesRequired);
       procedure UpdateImages;
@@ -32,12 +32,12 @@ type
       procedure FileButtonClick(Sender: TObject);
       procedure UninsDeleteKeyIfEmptyCheckClick(Sender: TObject);
       procedure MinVerCheckClick(Sender: TObject);
-      procedure MinVerDocImageClick(Sender: TObject);
+      procedure MinVerDocBitBtnClick(Sender: TObject);
     public
       constructor Create(const Form: TForm; const FileEdit: TEdit;
         const FileButton: TButton; const UninsDeleteKeyCheck,
         UninsDeleteKeyIfEmptyCheck, UninsDeleteValueCheck, MinVerCheck: TCheckBox;
-        const MinVerEdit: TEdit; const MinVerDocImage: TImage);
+        const MinVerEdit: TEdit; const MinVerDocBitBtn: TBitmapButton);
       procedure AddScript(var Registry: String; const AllowException: Boolean);
       property PrivilegesRequired: TPrivilegesRequired write SetPrivilegesRequired;
     end;
@@ -67,13 +67,13 @@ procedure TWizardFormRegistryHelper.UpdateImages;
 begin
  { After a DPI change the button's Width and Height isn't yet updated, so calculate it ourselves }
   var WH := MulDiv(16, FForm.CurrentPPI, 96);
-  FMinVerDocImage.Picture.Graphic:= GetImage(MainForm.HelpButton, WH);
+  FMinVerDocBitBtn.Bitmap.Assign(GetImage(MainForm.HelpButton, WH));
 end;
 
 constructor TWizardFormRegistryHelper.Create(const Form: TForm;
   const FileEdit: TEdit; const FileButton: TButton; const UninsDeleteKeyCheck,
   UninsDeleteKeyIfEmptyCheck, UninsDeleteValueCheck, MinVerCheck: TCheckBox;
-  const MinVerEdit: TEdit; const MinVerDocImage: TImage);
+  const MinVerEdit: TEdit; const MinVerDocBitBtn: TBitmapButton);
 begin
   FForm := Form;
   FFileEdit := FileEdit;
@@ -82,14 +82,14 @@ begin
   FUninsDeleteValueCheck := UninsDeleteValueCheck;
   FMinVerCheck := MinVerCheck;
   FMinVerEdit := MinVerEdit;
-  FMinVerDocImage := MinVerDocImage;
+  FMinVerDocBitBtn := MinVerDocBitBtn;
 
   FileButton.OnClick := FileButtonClick;
   UninsDeleteKeyIfEmptyCheck.OnClick := UninsDeleteKeyIfEmptyCheckClick;
   MinVerCheck.OnClick := MinVerCheckClick;
   MinVerCheck.OnClick(nil);
-  MinVerDocImage.OnClick := MinVerDocImageClick;
-  MinVerDocImage.Cursor := crHandPoint;
+  MinVerDocBitBtn.OnClick := MinVerDocBitBtnClick;
+  MinVerDocBitBtn.Cursor := crHandPoint;
 
   TryEnableAutoCompleteFileSystem(FileEdit.Handle);
 
@@ -119,12 +119,12 @@ end;
 procedure TWizardFormRegistryHelper.MinVerCheckClick(Sender: TObject);
 begin
   FMinVerEdit.Enabled := FMinVerCheck.Checked;
-  FMinVerDocImage.Visible := FMinVerCheck.Checked;
+  FMinVerDocBitBtn.Visible := FMinVerCheck.Checked;
   if FMinVerEdit.Enabled then
     FForm.ActiveControl := FMinVerEdit;
 end;
 
-procedure TWizardFormRegistryHelper.MinVerDocImageClick(Sender: TObject);
+procedure TWizardFormRegistryHelper.MinVerDocBitBtnClick(Sender: TObject);
 begin
   if Assigned(HtmlHelp) then
     HtmlHelp(GetDesktopWindow, PChar(GetHelpFile), HH_DISPLAY_TOPIC, Cardinal(PChar('topic_winvernotes.htm')));

+ 8 - 2
Projects/Src/Setup.ScriptClasses.pas

@@ -25,7 +25,7 @@ uses
   uPSR_stdctrls, uPSR_extctrls, uPSR_comobj,
   NewStaticText, NewCheckListBox, NewProgressBar, RichEditViewer,
   ExtCtrls, UIStateForm, Setup.SetupForm, Setup.MainForm, Setup.WizardForm, Shared.SetupTypes, PasswordEdit,
-  FolderTreeView, BitmapImage, NewNotebook, Setup.ScriptDlg, BidiCtrls,
+  FolderTreeView, BitmapButton, BitmapImage, NewNotebook, Setup.ScriptDlg, BidiCtrls,
   Setup.UninstallProgressForm;
 
 type
@@ -138,12 +138,17 @@ end;
 procedure TBitmapAlphaFormat_W(Self: TBitmap; const T: TAlphaFormat); begin Self.AlphaFormat := T; end;
 procedure TBitmapAlphaFormat_R(Self: TBitmap; var T: TAlphaFormat); begin T := Self.AlphaFormat; end;
 
-procedure RegisterBitmapImage_R(Cl: TPSRuntimeClassImporter);
+procedure RegisterBitmapButton_R(Cl: TPSRuntimeClassImporter);
 begin
   with Cl.FindClass('TBitmap') do
   begin
     RegisterPropertyHelper(@TBitmapAlphaFormat_R, @TBitmapAlphaFormat_W, 'AlphaFormat');
   end;
+  Cl.Add(TBitmapButton);
+end;
+
+procedure RegisterBitmapImage_R(Cl: TPSRuntimeClassImporter);
+begin
   Cl.Add(TBitmapImage);
 end;
 
@@ -431,6 +436,7 @@ begin
     RegisterCustomFolderTreeView_R(Cl);
     RegisterFolderTreeView_R(Cl);
     RegisterStartMenuFolderTreeView_R(Cl);
+    RegisterBitmapButton_R(Cl);
     RegisterBitmapImage_R(Cl);
     RegisterBidiCtrls_R(Cl);
 

+ 6 - 1
Projects/Src/Setup.ScriptFunc.pas

@@ -21,7 +21,7 @@ implementation
 uses
   Windows,
   Forms, SysUtils, Classes, Graphics, ActiveX, Generics.Collections,
-  uPSUtils, PathFunc, ISSigFunc, ECDSA, BrowseFunc, MD5, SHA1, SHA256, BitmapImage, PSStackHelper,
+  uPSUtils, PathFunc, ISSigFunc, ECDSA, BrowseFunc, MD5, SHA1, SHA256, BitmapButton, BitmapImage, PSStackHelper,
   Shared.Struct, Setup.ScriptDlg, Setup.MainFunc, Shared.CommonFunc.Vcl,
   Shared.CommonFunc, Shared.FileClass, SetupLdrAndSetup.RedirFunc,
   Setup.Install, SetupLdrAndSetup.InstFunc, Setup.InstFunc, Setup.InstFunc.Ole,
@@ -1814,6 +1814,11 @@ var
       if ErrorCode <> 0 then
         raise Exception.Create(Win32ErrorString(ErrorCode));
     end);
+    RegisterScriptFunc('INITIALIZEBITMAPBUTTONFROMICON', procedure(const Caller: TPSExec; const OrgName: AnsiString; const Stack: TPSStack; const PStart: Cardinal)
+    begin
+      var AscendingTrySizes := Stack.GetIntArray(PStart-4);
+      Stack.SetBool(PStart, TBitmapButton(Stack.GetClass(PStart-1)).InitializeFromIcon(0, PChar(Stack.GetString(PStart-2)), Stack.GetInt(PStart-3), AscendingTrySizes));
+    end);
     RegisterScriptFunc('INITIALIZEBITMAPIMAGEFROMICON', procedure(const Caller: TPSExec; const OrgName: AnsiString; const Stack: TPSStack; const PStart: Cardinal)
     begin
       var AscendingTrySizes := Stack.GetIntArray(PStart-4);

+ 1 - 0
Projects/Src/Shared.ScriptFunc.pas

@@ -539,6 +539,7 @@ initialization
     'function CreateCallback(Method: AnyMethod): Longword;',
     'function IsDotNetInstalled(const MinVersion: TDotNetVersion; const MinServicePack: Cardinal): Boolean;',
     'function IsMsiProductInstalled(const UpgradeCode: String; const PackedMinVersion: Int64): Boolean;',
+    'function InitializeBitmapButtonFromIcon(const BitmapButton: TBitmapButton; const IconFilename: String; const BkColor: TColor; const AscendingTrySizes: TArrayOfInteger): Boolean;',
     'function InitializeBitmapImageFromIcon(const BitmapImage: TBitmapImage; const IconFilename: String; const BkColor: TColor; const AscendingTrySizes: TArrayOfInteger): Boolean;',
     'procedure Extract7ZipArchive(const ArchiveFileName, DestDir: String; const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);',
     'procedure ExtractArchive(const ArchiveFilename, DestDir, Password: String; const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);',

+ 1 - 0
README.md

@@ -84,6 +84,7 @@ the following component units, which can be found in the [Components]
 directory.
 
 - BidiCtrls
+- BitmapButton
 - BitmapImage
 - FolderTreeView
 - NewCheckListBox

+ 29 - 25
isdonateandmail.iss

@@ -7,18 +7,20 @@ Source: "ismail.bmp"; Flags: dontcopy noencryption
 
 [CustomMessages]
 ; No need to localize: The IS website is in English only
+IsDonateAndMailDonateCaption=Donate
 IsDonateAndMailDonateHint=Support Inno Setup - Thank you!
+IsDonateAndMailMailCaption=Subscribe
 IsDonateAndMailMailHint=Be notified by e-mail of new Inno Setup releases
 
 [Code]
-procedure DonateImageOnClick(Sender: TObject);
+procedure DonateBitmapButtonOnClick(Sender: TObject);
 var
   ErrorCode: Integer;
 begin
   ShellExecAsOriginalUser('open', 'https://jrsoftware.org/isdonate.php', '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);
 end;
 
-procedure MailImageOnClick(Sender: TObject);
+procedure MailBitmapButtonOnClick(Sender: TObject);
 var
   ErrorCode: Integer;
 begin
@@ -29,7 +31,7 @@ end;
 procedure IsDonateAndMailInitializeWizard;
 var
   ImageFileName: String;
-  DonateImage, MailImage: TBitmapImage;
+  DonateBitmapButton, MailBitmapButton: TBitmapButton;
   BevelTop: Integer;
 begin
   if WizardSilent then
@@ -38,31 +40,33 @@ begin
   ImageFileName := ExpandConstant('{tmp}\isdonate.bmp');
   ExtractTemporaryFile(ExtractFileName(ImageFileName));
 
-  DonateImage := TBitmapImage.Create(WizardForm);
-  DonateImage.AutoSize := True;
-  DonateImage.Bitmap.LoadFromFile(ImageFileName);
-  DonateImage.Hint := CustomMessage('IsDonateAndMailDonateHint');
-  DonateImage.ShowHint := True;
-  DonateImage.Anchors := [akLeft, akBottom];
+  DonateBitmapButton := TBitmapButton.Create(WizardForm);
+  DonateBitmapButton.AutoSize := True;
+  DonateBitmapButton.Bitmap.LoadFromFile(ImageFileName);
+  DonateBitmapButton.Caption := CustomMessage('IsDonateAndMailDonateCaption');
+  DonateBitmapButton.Hint := CustomMessage('IsDonateAndMailDonateHint');
+  DonateBitmapButton.ShowHint := True;
+  DonateBitmapButton.Anchors := [akLeft, akBottom];
   BevelTop := WizardForm.Bevel.Top;
-  DonateImage.Top := BevelTop + (WizardForm.ClientHeight - BevelTop - DonateImage.Bitmap.Height) div 2;
-  DonateImage.Left := DonateImage.Top - BevelTop;
-  DonateImage.Cursor := crHand;
-  DonateImage.OnClick := @DonateImageOnClick;
-  DonateImage.Parent := WizardForm;
+  DonateBitmapButton.Top := BevelTop + (WizardForm.ClientHeight - BevelTop - DonateBitmapButton.Bitmap.Height) div 2;
+  DonateBitmapButton.Left := DonateBitmapButton.Top - BevelTop;
+  DonateBitmapButton.Cursor := crHand;
+  DonateBitmapButton.OnClick := @DonateBitmapButtonOnClick;
+  DonateBitmapButton.Parent := WizardForm;
 
   ImageFileName := ExpandConstant('{tmp}\ismail.bmp');
   ExtractTemporaryFile(ExtractFileName(ImageFileName));
 
-  MailImage := TBitmapImage.Create(WizardForm);
-  MailImage.AutoSize := True;
-  MailImage.Bitmap.LoadFromFile(ImageFileName);
-  MailImage.Hint := CustomMessage('IsDonateAndMailMailHint');
-  MailImage.ShowHint := True;
-  MailImage.Anchors := [akLeft, akBottom];
-  MailImage.Top := DonateImage.Top
-  MailImage.Left := DonateImage.Left + DonateImage.Width + ScaleX(8);
-  MailImage.Cursor := crHand;
-  MailImage.OnClick := @MailImageOnClick;
-  MailImage.Parent := WizardForm;
+  MailBitmapButton := TBitmapButton.Create(WizardForm);
+  MailBitmapButton.AutoSize := True;
+  MailBitmapButton.Bitmap.LoadFromFile(ImageFileName);
+  MailBitmapButton.Caption := CustomMessage('IsDonateAndMailMailCaption');
+  MailBitmapButton.Hint := CustomMessage('IsDonateAndMailMailHint');
+  MailBitmapButton.ShowHint := True;
+  MailBitmapButton.Anchors := [akLeft, akBottom];
+  MailBitmapButton.Top := DonateBitmapButton.Top
+  MailBitmapButton.Left := DonateBitmapButton.Left + DonateBitmapButton.Width + ScaleX(4);
+  MailBitmapButton.Cursor := crHand;
+  MailBitmapButton.OnClick := @MailBitmapButtonOnClick;
+  MailBitmapButton.Parent := WizardForm;
 end;

+ 3 - 1
whatsnew.htm

@@ -210,6 +210,7 @@ issigtool --key-file="MyKey.ispublickey" verify "MyProg.dll"</code></pre>
       <li>The <i>Highlight occurrences of current word</i> option (which is disabled by default) no longer highlights a section's directive names, parameter names, or Pascal keywords. The <i>Highlight occurrences of current selection</i> option (which is enabled by default) still does.</li>
       <li>Dark mode is now supported throughout.</li>
       <li>Improved support for high-contrast themes.</li>
+      <li>Improved support for screen readers.</li>
     </ul>
   </li>
   <li><tt>[Files]</tt> section parameter <tt>Excludes</tt> can now be combined with the <tt>external</tt> flag.</li>
@@ -225,8 +226,9 @@ issigtool --key-file="MyKey.ispublickey" verify "MyProg.dll"</code></pre>
   </li>
   <li>Pascal Scripting changes:
     <ul>
+      <li>Added new <tt>TBitmapButton</tt> support class which works just like <tt>TBitmapImage</tt>, but is accessible by keyboard and compatible with screen readers. Make sure to set the <tt>Caption</tt> property, even if it isn't visible. See updated example script <i>CodeClasses.iss</i> for an example.</li>
+      <li>Added new <tt>InitializeBitmapButtonFromIcon</tt> and <tt>GetSHA256OfStream</tt> support functions.</li>
       <li>Added new <tt>LastBaseNameOrUrl</tt> property to support class <tt>TDownloadWizardPage</tt>. See updated example script <i>CodeDownloadFiles.iss</i> for an example.</li>
-      <li>Added new <tt>GetSHA256OfStream</tt> support function.</li>
       <li><i>Fix:</i> Event function <tt>CurPageChanged</tt> is now always only triggered when the current page actually changes. Before it was called twice in a row for <tt>wpPreparing</tt> when the script had a <tt>PrepareToInstall</tt> event function which returned a non empty string to instruct Setup to stop.</li>
     </ul>
   </li>