Search code examples
installationnsis

NSIS optional custom pages


I want to create a checkbox in NSIS at the end of the installation process, when checked, it will lead the user to a custom page that has a bunch of radioboxes that lead the user to another custom page (depending on the choice) and so on.

I know how to create a custom page using nsDialogs, but I can't figure out the logic behind multiple optional custom pages. Any help is appreciated.

Alternatively, is there a way to create multiple checkbox/radiobox groups that are disabled by default (except the first one) and only activated when the user selects a box in a group before it?


Solution

  • Pages are skipped by calling Abort in the page Pre-callback. Then all you have to do is keep track of which page you want to show and which you want to skip:

    !include nsDialogs.nsh
    !include LogicLib.nsh
    
    Section "Do custom page thing" SID_DOCUSTOMPAGES
    SectionEnd
    
    Page Components
    Page InstFiles
    Page Custom Cust1Pre Cust2Next
    Page Custom CustAPre
    Page Custom CustBPre
    
    Var WantedPage
    
    Function Cust1Pre
    ${IfNot} ${SectionIsSelected} ${SID_DOCUSTOMPAGES}
        StrCpy $WantedPage ""
        Abort
    ${EndIf}
    nsDialogs::Create 1018
    Pop $0
    ${NSD_CreateRadioButton} 0 30u 100% 10u "Page A"
    Pop $1
    ${NSD_Check} $1
    ${NSD_CreateRadioButton} 0 40u 100% 10u "Page B"
    Pop $2
    nsDialogs::Show
    FunctionEnd
    
    Function Cust2Next
    ${NSD_GetState} $1 $0
    ${If} $0 <> ${BST_UNCHECKED}
        StrCpy $WantedPage A
    ${Else}
        StrCpy $WantedPage B
    ${EndIf}
    FunctionEnd
    
    Function CustAPre
    ${IfNotThen} $WantedPage == A ${|} Abort ${|}
    GetDlgItem $0 $hWndParent 1
    SendMessage $0 ${WM_SETTEXT} "" "STR:$(^CloseBtn)" ; Change button text since page B is skipped and we are now the last page
    nsDialogs::Create 1018
    Pop $0
    ${NSD_CreateButton} 0 30u 100% 10u "Page A"
    Pop $0
    nsDialogs::Show
    FunctionEnd
    
    Function CustBPre
    ${IfNotThen} $WantedPage == B ${|} Abort ${|}
    nsDialogs::Create 1018
    Pop $0
    ${NSD_CreateButton} 0 30u 100% 10u "Page B"
    Pop $0
    nsDialogs::Show
    FunctionEnd
    

    You can get rid of the WM_SETTEXT workaround by having a single page that is "A" or "B":

    !include nsDialogs.nsh
    !include LogicLib.nsh
    
    Section "Do custom page thing" SID_DOCUSTOMPAGES
    SectionEnd
    
    Page Components
    Page InstFiles
    Page Custom Cust1Pre Cust2Next
    Page Custom CustAOrBPre
    
    Var WantedPage
    
    Function Cust1Pre
    ${IfNot} ${SectionIsSelected} ${SID_DOCUSTOMPAGES}
        StrCpy $WantedPage ""
        Abort
    ${EndIf}
    nsDialogs::Create 1018
    Pop $0
    ${NSD_CreateRadioButton} 0 30u 100% 10u "Page A"
    Pop $1
    ${NSD_Check} $1
    ${NSD_CreateRadioButton} 0 40u 100% 10u "Page B"
    Pop $2
    nsDialogs::Show
    FunctionEnd
    
    Function Cust2Next
    ${NSD_GetState} $1 $0
    ${If} $0 <> ${BST_UNCHECKED}
        StrCpy $WantedPage A
    ${Else}
        StrCpy $WantedPage B
    ${EndIf}
    FunctionEnd
    
    Function CustAPre
    nsDialogs::Create 1018
    Pop $0
    ${NSD_CreateButton} 0 30u 100% 10u "Page A"
    Pop $0
    nsDialogs::Show
    FunctionEnd
    
    Function CustBPre
    nsDialogs::Create 1018
    Pop $0
    ${NSD_CreateButton} 0 30u 100% 10u "Page B"
    Pop $0
    nsDialogs::Show
    FunctionEnd
    
    Function CustAOrBPre
    ${If} $WantedPage == A
        Call CustAPre
    ${Else}
        Call CustBPre
    ${EndIf}
    FunctionEnd
    

    Having just a single page with disabled options is of course better:

    !include nsDialogs.nsh
    !include LogicLib.nsh
    
    Page InstFiles
    Page Custom CustPre
    
    Function CustPre
    nsDialogs::Create 1018
    Pop $0
    
    ${NSD_CreateCheckBox} 10 30u 100% 10u "Enable other stuff"
    Pop $0
    ${NSD_OnClick} $0 OnCheckChange
    
    ${NSD_CreateRadioButton} 10 50u 100% 10u "Foo"
    Pop $1
    ${NSD_AddStyle} $1 ${WS_GROUP}
    ${NSD_Check} $1
    ${NSD_CreateRadioButton} 10 70u 100% 10u "Bar"
    Pop $2
    ${NSD_RemoveStyle} $2 ${WS_TABSTOP}
    
    Push $0
    Call OnCheckChange ; Enforce state
    nsDialogs::Show
    FunctionEnd
    
    !macro SetRadiosEnabled state
    EnableWindow $1 ${state}
    EnableWindow $2 ${state}
    !macroend
    Function OnCheckChange
    Pop $0
    ${NSD_GetState} $0 $0
    ${If} $0 <> ${BST_UNCHECKED}
        !insertmacro SetRadiosEnabled 1
    ${Else}
        !insertmacro SetRadiosEnabled 0
    ${EndIf}
    FunctionEnd