Search code examples
inno-setuppascalpascalscript

Inno Setup - Dynamic positioning of controls/checkboxes


I have a bunch of checkboxes on a page, and all of those are conditionally visible, and the Top position is defined relative to the previous checkbox, e.g.

CheckBox4.Top := CheckBox3.Top + CheckBox3.Height + 5;

When at least one of the components is set to be invisible, the result looks like this:

Gap

What I'd like the checkboxes to do, is to shift upwards, if the previous one is set to invisible. Prog3 should be right underneath Prog1, or, if both Prog2 and Prog3 are hidden, Prog4 should be right underneath Prog1.


EDIT: My code after the answer:

var
  PageNameLabel,PageDescriptionLabel: TLabel;
  TypesComboOnChangePrev: TNotifyEvent;
  UninstallConfigssPage: TNewNotebookPage;
  UninstallNextButton: TNewButton;
  CheckListBox: TNewCheckListBox;
  Dirs: TStringList;

procedure UpdateUninstallWizard;
begin
  UninstallProgressForm.PageNameLabel.Caption := CustomMessage('UninstPNL');
  UninstallProgressForm.PageDescriptionLabel.Caption := CustomMessage('UninstPDL');
  UninstallNextButton.Caption := CustomMessage('UninstBtn');
  UninstallNextButton.ModalResult := mrOK;
end;  

procedure UninstallNextButtonClick(Sender: TObject);
begin
  UninstallNextButton.Visible := False;
end;

procedure AddDirCheckbox(Path: string; Caption: string);
begin
  if DirExists(Path) then
  begin
    Dirs.Add(Path);
    CheckListBox.AddCheckBox(Caption, '', 0, False, True, False, False, nil);
  end;
end;

procedure InitializeUninstallProgressForm();
var
  PageText: TNewStaticText;
  PageNameLabel,PageDescriptionLabel: string;
  CancelButtonEnabled: Boolean;
  CancelButtonModalResult: Integer;
begin
  if not UninstallSilent then
  begin
    UninstallProgressForm.Caption := CustomMessage('Uninst');

    UninstallConfigssPage:= TNewNotebookPage.Create(UninstallProgressForm);
    UninstallConfigssPage.Notebook := UninstallProgressForm.InnerNotebook;
    UninstallConfigssPage.Parent := UninstallProgressForm.InnerNotebook;
    UninstallConfigssPage.Align := alClient;

    PageText := TNewStaticText.Create(UninstallProgressForm);
    PageText.Parent := UninstallConfigssPage;
    PageText.Top := UninstallProgressForm.StatusLabel.Top;
    PageText.Left := UninstallProgressForm.StatusLabel.Left - ScaleX(20);
    PageText.Width := UninstallProgressForm.StatusLabel.Width;
    PageText.Height := UninstallProgressForm.StatusLabel.Height;
    PageText.AutoSize := True;
    PageText.ShowAccelChar := False;
    PageText.Caption := CustomMessage('UninstTxt');

    Dirs := TStringList.Create();
    CheckListBox := TNewCheckListBox.Create(UninstallConfigssPage);
    CheckListBox.Parent := UninstallConfigssPage;
    CheckListBox.SetBounds(PageText.Left,ScaleY(30),PageText.Width,ScaleY(220));
    CheckListBox.BorderStyle := bsNone;
    CheckListBox.Color := clBtnFace;
    CheckListBox.MinItemHeight := ScaleY(20);

    AddDirCheckbox(ExpandConstant('{app}\Alien Shooter'), 'Prog1');
    AddDirCheckbox(ExpandConstant('{app}\Theseus'), 'Prog2');
    AddDirCheckbox(ExpandConstant('{app}\Alien Shooter Revisited'), 'Prog3');
    AddDirCheckbox(ExpandConstant('{app}\Alien Shooter 2'), 'Prog4');
    AddDirCheckbox(ExpandConstant('{app}\Alien Shooter 2 Reloaded'), 'Prog5');
    AddDirCheckbox(ExpandConstant('{app}\Alien Shooter 2 Conscription'), 'Prog6');
    AddDirCheckbox(ExpandConstant('{app}\Zombie Shooter'), 'Prog7');
    AddDirCheckbox(ExpandConstant('{app}\Zombie Shooter 2'), 'Prog8');

    UninstallProgressForm.InnerNotebook.ActivePage := UninstallConfigssPage;

    PageNameLabel := UninstallProgressForm.PageNameLabel.Caption;
    PageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption;

    UninstallNextButton := TNewButton.Create(UninstallProgressForm);
    UninstallNextButton.Parent := UninstallProgressForm;
    UninstallNextButton.Left := UninstallProgressForm.CancelButton.Left - UninstallProgressForm.CancelButton.Width - ScaleX(35);
    UninstallNextButton.Top := UninstallProgressForm.CancelButton.Top;
    UninstallNextButton.Width := UninstallProgressForm.CancelButton.Width + ScaleX(30);
    UninstallNextButton.Height := UninstallProgressForm.CancelButton.Height;
    UninstallNextButton.OnClick := @UninstallNextButtonClick;
    UninstallNextButton.TabOrder := UninstallProgressForm.CancelButton.TabOrder - 1;

    UpdateUninstallWizard;
    CancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled
    UninstallProgressForm.CancelButton.Enabled := True;
    CancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult;
    UninstallProgressForm.CancelButton.ModalResult := mrCancel;

    if UninstallProgressForm.ShowModal = mrCancel then Abort;

    UninstallProgressForm.CancelButton.Enabled := CancelButtonEnabled;
    UninstallProgressForm.CancelButton.ModalResult := CancelButtonModalResult;

    UninstallProgressForm.PageNameLabel.Caption := PageNameLabel;
    UninstallProgressForm.PageDescriptionLabel.Caption := PageDescriptionLabel;

    UninstallProgressForm.InnerNotebook.ActivePage := UninstallProgressForm.InstallingPage;
  end;
end;

Solution

  • Use TInputOptionWizardPage, which is designed for this kind of tasks/layouts. Create it using CreateInputOptionPage.

    Use TStringList (or array of string) to maintain association between the created checkboxes and the paths.

    var
      Page: TInputOptionWizardPage;
      Dirs: TStringList;
    
    procedure AddDirCheckbox(Path: string; Caption: string);
    begin
      if DirExists(Path) then
      begin
        Dirs.Add(Path);
        Page.Add(Caption);
      end;
    end;
    
    procedure InitializeWizard();
    begin
      Page :=
        CreateInputOptionPage(
          wpWelcome, 'Configuration files found', 'Choose an action for configuration files',
          'Choose the configuration files you''d like to be deleted.', False, False);
      Dirs := TStringList.Create();
      AddDirCheckbox('C:\dir1', 'Prog 1');
      AddDirCheckbox('C:\dir2', 'Prog 2');
      AddDirCheckbox('C:\dir3', 'Prog 3');
    end;
    

    To process the selected checkboxes/paths, use a code like this:

    procedure CurStepChanged(CurStep: TSetupStep);
    var
      Index: Integer;
    begin
      if CurStep = ssInstall then
      begin
        for Index := 0 to Dirs.Count - 1 do
        begin
          if Page.Values[Index] then
          begin
            MsgBox(Format('Deleting %s', [Dirs[Index]]), mbInformation, MB_OK);
          end;
        end;
      end;
    end;
    

    Assuming C:\dir1 and C:\dir3 exist and C:\dir2 does not, you will get:

    Check list box


    If you cannot use TInputOptionWizardPage, e.g. because you need the checkboxes on an uninstaller form or on a custom form, just create the TNewCheckListBox (what TInputOptionWizardPage uses internally).

    The following example places the TNewCheckListBox on a blank custom TWizardPage, but you can of course place it, wherever you want.

    var
      Page: TWizardPage;
      CheckListBox: TNewCheckListBox;
      Dirs: TStringList;
    
    procedure AddDirCheckbox(Path: string; Caption: string);
    begin
      if DirExists(Path) then
      begin
        Dirs.Add(Path);
        CheckListBox.AddCheckBox(Caption, '', 0, False, True, False, False, nil);
      end;
    end;
    
    procedure InitializeWizard();
    begin
      Page :=
        CreateCustomPage(
          wpWelcome, 'Configuration files found',
          'Choose an action for configuration files');
      Dirs := TStringList.Create();
      CheckListBox := TNewCheckListBox.Create(Page);
      CheckListBox.Parent := Page.Surface;
      CheckListBox.Width := Page.SurfaceWidth;
      CheckListBox.Height := Page.SurfaceHeight;
    
      { The same styling as used by TInputOptionWizardPage }
      CheckListBox.BorderStyle := bsNone;
      CheckListBox.Color := clBtnFace;
      CheckListBox.WantTabs := True;
      CheckListBox.MinItemHeight := ScaleY(22);
    
      AddDirCheckbox('C:\dir1', 'Prog 1');
      AddDirCheckbox('C:\dir2', 'Prog 2');
      AddDirCheckbox('C:\dir3', 'Prog 3');
    end;
    
    procedure CurStepChanged(CurStep: TSetupStep);
    var
      Index: Integer;
    begin
      if CurStep = ssInstall then
      begin
        for Index := 0 to Dirs.Count - 1 do
        begin
          if CheckListBox.Checked[Index] then
          begin
            MsgBox(Format('Deleting %s', [Dirs[Index]]), mbInformation, MB_OK);
          end;
        end;
      end;
    end;
    

    enter image description here