NsDialogs FAQ: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
Line 263: Line 263:
Section ""
Section ""
SectionEnd
SectionEnd
</highlight-nsis>
'''NOTE''' to re-set the 'fat dot' character seen in the ANSI build when using 'XPStyle on', you cannot use the standard SendMessage (using SendMessageA under the hood) as it is a Unicode character.  The System plugin can be used to send a SendMessageW command instead:
<highlight-nsis>
/* 9679 is the fat dot char */
System::Call "user32::SendMessageW(i $hwnd, i ${EM_SETPASSWORDCHAR}, i 9679, i 0)"
</highlight-nsis>
</highlight-nsis>



Revision as of 16:58, 20 October 2010

How to Enable/Disable a control

Use the standard NSIS EnableWindow command.

NSDialogs lets you pop the hwnd of a control created via ${NSD_Create*}. EnableWindow takes a hwnd as one of its parameters. With this, you can easily enable/disable a control.

!include "nsDialogs.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
OutFile "test.exe"
 
Page Custom pre
 
var dialog
var hwnd
var button
 
Function pre
	nsDialogs::Create 1018
	Pop $dialog
	${NSD_CreateCheckbox} 0 0 50% 6% "Enable button below"
		Pop $hwnd
		${NSD_OnClick} $hwnd EnDisableButton
 
	${NSD_CreateButton} 25% 25% 50% 50% "Hello World"
		Pop $button
		EnableWindow $button 0 # start out disabled
 
	nsDialogs::Show
FunctionEnd
 
Function EnDisableButton
	Pop $hwnd
	${NSD_GetState} $hwnd $0
	${If} $0 == 1
		EnableWindow $button 1
	${Else}
		EnableWindow $button 0
	${EndIf}
FunctionEnd
 
Section ""
SectionEnd

How to Show/Hide a control

Use the standard NSIS ShowWindow command.

NSDialogs lets you pop the hwnd of a control created via ${NSD_Create*}. ShowWindow takes a hwnd as one of its parameters. With this, you can easily show/hide a control.

!include "nsDialogs.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
OutFile "test.exe"
 
Page Custom pre
 
var dialog
var hwnd
var button
 
Function pre
	nsDialogs::Create 1018
		Pop $dialog
	${NSD_CreateCheckbox} 0 0 50% 6% "Show button below"
		Pop $hwnd
		${NSD_OnClick} $hwnd EnDisableButton
 
	${NSD_CreateButton} 25% 25% 50% 50% "Hello World"
		Pop $button
		ShowWindow $button ${SW_HIDE} # start out hidden
 
	nsDialogs::Show
FunctionEnd
 
Function EnDisableButton
	Pop $hwnd
	${NSD_GetState} $hwnd $0
	${If} $0 == 1
		ShowWindow $button ${SW_SHOW}
	${Else}
		ShowWindow $button ${SW_HIDE}
	${EndIf}
FunctionEnd
 
Section ""
SectionEnd

How to create a label with center-aligned text

Center-aligned text - where the text is always centered within the control, no matter its size, is often useful for presentation purposes. Making a label's text center-aligned is simple, using ${NSD_AddStyle} add the ${SS_CENTER} style.

!include "nsDialogs.nsh"
 
OutFile "$%temp%\temp.exe"
 
var dialog
var hwnd
var null
 
Page custom Test
 
Function Test
    nsDialogs::Create 1018
        Pop $dialog
 
    ${NSD_CreateLabel} 0 0 100% 20% "This line will be centered.$\nAnd so will this line."
        Pop $hwnd
        ${NSD_AddStyle} $hwnd ${SS_CENTER}
 
    nsDialogs::Show
FunctionEnd
 
Section
SectionEnd

How to create a multi-line edit (text) control

Although multi-line is a Style of a control, for Edit (Text) controls it is one of few styles that cannot be set once the control has already been created - so you can't use ${NSD_AddStyle}.

Instead, you will have to create the control manually:

!include "nsDialogs.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
OutFile "test.exe"
 
Page Custom pre
 
var dialog
var hwnd
 
Function pre
	nsDialogs::Create 1018
		Pop $dialog
	${NSD_CreateText} 0 0 100% 40% "This is NOT a$\r$\nmulti-line$\r$\nedit control"
		Pop $hwnd
 
	nsDialogs::CreateControl EDIT \
		"${__NSD_Text_STYLE}|${WS_VSCROLL}|${ES_MULTILINE}|${ES_WANTRETURN}" \
		"${__NSD_Text_EXSTYLE}" \
		0 50% 100% 40% \
		"This IS a$\r$\nmulti-line$\r$\nedit control"
		Pop $hwnd
 
	nsDialogs::Show
FunctionEnd
 
Section ""
SectionEnd

Or, much cleaner, using the NsDialogs_CreateTextMultiline header:

!include "nsDialogs.nsh"
!include "nsDialogs_createTextMultiline.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
OutFile "test.exe"
 
Page Custom pre
 
var dialog
var hwnd
 
Function pre
	nsDialogs::Create 1018
		Pop $dialog
	${NSD_CreateText} 0 0 100% 40% "This is NOT a$\r$\nmulti-line$\r$\nedit control"
		Pop $hwnd
 
	${NSD_CreateTextMultiline} 0 50% 100% 40% "This IS a$\r$\nmulti-line$\r$\nedit control"
		Pop $hwnd
 
	nsDialogs::Show
FunctionEnd
 
Section ""
SectionEnd

How to create a Text Password control

See the nsDialogs documentation - a Password control is one of the controls supported by default; ${NSD_CreatePassword}

How to set the character used by a Text Password control

By default a Text Password control will use asterisks (*****) with 'XPStyle off', or fat dots with 'XPStyle on' as the password characters to mask the text. You can change this character using SendMessage with the EM_SETPASSWORDCHAR flag.

The password character is identified by its ASCII or UNICODE index; keep in mind that the font you use for your installer may not support the character you wish to use.

NOTE If you set the password character to 0 (zero), the text will not be masked! You can use this to toggle between the text being visible and masked. See also the How to hide/show passwords in a Text Password control entry.

!include "nsDialogs.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
OutFile "test.exe"
 
Page Custom pre
 
var dialog
var hwnd
 
Function pre
	nsDialogs::Create 1018
		Pop $dialog
	${NSD_CreatePassword} 0 0 50% 8% "This is a password field"
	    Pop $hwnd
	    SendMessage $hwnd ${EM_SETPASSWORDCHAR} 149 0 # 149 = medium dot
 
	nsDialogs::Show
FunctionEnd
 
Section ""
SectionEnd

How to hide/show passwords in a Text Password control

By using SendMessage with the EM_SETPASSWORDCHAR flag, specifying the character as 0 (zero), you can make a password visible, and invisible again by specifying a non-zero character.

See also the How to set the character used by a Text Password control entry.

NOTE The Password control has to be forcibly redrawn after changing the mask character. This is done by hiding and then showing the control using ShowWindow.

!include "nsDialogs.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
OutFile "test.exe"
 
Page Custom pre
 
var dialog
var hwnd
var passwordControl
 
Function pre
	nsDialogs::Create 1018
		Pop $dialog
 
	${NSD_CreateCheckbox} 0 0 25% 8% "Show password"
		Pop $hwnd
		${NSD_OnClick} $hwnd ShowPassword
 
	${NSD_CreatePassword} 0 10% 50% 8% "This is a password field"
	    Pop $passwordControl
 
	nsDialogs::Show
FunctionEnd
 
Function ShowPassword
	Pop $hwnd
	${NSD_GetState} $hwnd $0
	ShowWindow $passwordControl ${SW_HIDE}
	${If} $0 == 1
		SendMessage $passwordControl ${EM_SETPASSWORDCHAR} 0 0
	${Else}
		SendMessage $passwordControl ${EM_SETPASSWORDCHAR} 42 0
	${EndIf}
	ShowWindow $passwordControl ${SW_SHOW}
FunctionEnd
 
Section ""
SectionEnd

NOTE to re-set the 'fat dot' character seen in the ANSI build when using 'XPStyle on', you cannot use the standard SendMessage (using SendMessageA under the hood) as it is a Unicode character. The System plugin can be used to send a SendMessageW command instead:

/* 9679 is the fat dot char */
System::Call "user32::SendMessageW(i $hwnd, i ${EM_SETPASSWORDCHAR}, i 9679, i 0)"

How to create a Numbers-only Text control

See the nsDialogs documentation - a Numbers-only control is one of the controls supported by default; ${NSD_CreateNumber}

How to create a Read-only Text control

The read-only state of a Text control can be set using SendMessage with the EM_SETREADONLY flag.

Note that a read-only Edit (Text) control is not the same as a disabled Text control. A disabled Text control will display its text grayed out, a read-only Text control remains black on a light grey background. In addition, a disabled multi-line Text control cannot be scrolled. A read-only multi-line Text control, on the other hand, can be scrolled.

!include "nsDialogs.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
OutFile "test.exe"
 
Page Custom pre
 
var dialog
var hwnd
var textControl
 
Function pre
	nsDialogs::Create 1018
		Pop $dialog
	${NSD_CreateCheckBox} 0 0 50% 6% "Set text field below read-only"
	    Pop $hwnd
	    ${NSD_OnClick} $hwnd SetReadonly
	${NSD_CreateText} 0 12% 100% 8% "Hello World"
		Pop $textControl
 
	nsDialogs::Show
FunctionEnd
 
Function SetReadonly
	Pop $hwnd
	${NSD_GetState} $hwnd $0
	SendMessage $textControl ${EM_SETREADONLY} $0 0
FunctionEnd
 
Section ""
SectionEnd

How to create two groups of RadioButtons

Use ${NSD_AddStyle} to add the WS_GROUP style to the first radiobutton of each group. In Windows' UI handling, all radiobuttons are considered to be part of the same group if no group starter is defined.

Therefore, to have two radiobutton groups of two radiobuttons each, you must specify a group starter for each, otherwise the third and fourth radiobuttons will be considered part of the first group.

Setting a group starter is easy using ${NSD_AddStyle}, use:

${NSD_AddStyle} $hwnd ${WS_GROUP} # WS_GROUP is defined in winmessages.nsh

example:

!include "nsDialogs.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
OutFile "test.exe"
 
Page Custom pre
 
var dialog
var hwnd
 
Function pre
	nsDialogs::Create 1018
		Pop $dialog
	${NSD_CreateRadioButton} 0 0 40% 6% "Group 1, Radio 1"
		Pop $hwnd
		${NSD_AddStyle} $hwnd ${WS_GROUP}
	${NSD_CreateRadioButton} 0 12% 40% 6% "Group 1, Radio 2"
		Pop $hwnd
 
	${NSD_CreateRadioButton} 50% 0 40% 6% "Group 2, Radio 1"
		Pop $hwnd
		${NSD_AddStyle} $hwnd ${WS_GROUP}
	${NSD_CreateRadioButton} 50% 12% 40% 6% "Group 2, Radio 2"
		Pop $hwnd
 
	nsDialogs::Show
FunctionEnd
 
Section ""
SectionEnd

How to easily handle radiobutton selections

Often you do not want to specify separate OnClick functions for each radiobutton...

!include "nsDialogs.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
OutFile "test.exe"
 
Page Custom pre
 
var dialog
var hwnd
 
Function pre
	nsDialogs::Create 1018
		Pop $dialog
	${NSD_CreateRadioButton} 0 0 40% 6% "Group 1, Radio 1"
		Pop $hwnd
		${NSD_AddStyle} $hwnd ${WS_GROUP}
		${NSD_OnClick} $hwnd Group1Radio1Click
	${NSD_CreateRadioButton} 0 12% 40% 6% "Group 1, Radio 2"
		Pop $hwnd
		${NSD_OnClick} $hwnd Group1Radio2Click
 
	nsDialogs::Show
FunctionEnd
 
Function Group1Radio1Click
	Pop $hwnd
	MessageBox MB_OK "onClick:Group1Radio1"
FunctionEnd
Function Group1Radio2Click
	Pop $hwnd
	MessageBox MB_OK "onClick:Group1Radio2"
FunctionEnd
 
Section ""
SectionEnd

But the only obvious alternative seems to be to place each in a different variable and comparing the hwnd on the stack when the callback function is called to that variable.

!include "nsDialogs.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
OutFile "test.exe"
 
Page Custom pre
 
var dialog
var hwnd
 
var Group1Radio1
var Group1Radio2
 
Function pre
	nsDialogs::Create 1018
		Pop $dialog
	${NSD_CreateRadioButton} 0 0 40% 6% "Group 1, Radio 1"
		Pop $Group1Radio1
		${NSD_AddStyle} $Group1Radio1 ${WS_GROUP}
		${NSD_OnClick} $Group1Radio1 RadioClick
	${NSD_CreateRadioButton} 0 12% 40% 6% "Group 1, Radio 2"
		Pop $Group1Radio2
		${NSD_OnClick} $Group1Radio2 RadioClick
 
	nsDialogs::Show
FunctionEnd
 
Function RadioClick
	Pop $hwnd
	${If} $hwnd == $Group1Radio1
	    MessageBox MB_OK "onClick:Group1Radio1"
	${ElseIf} $hwnd == $Group1Radio2
	    MessageBox MB_OK "onClick:Group1Radio2"
	${EndIf}
FunctionEnd
 
Section ""
SectionEnd

However, you can eliminate both by making use of the getUserData and setUserData commands of nsDialogs. Using these *UserData commands, you can store data in a control and easily retrieve this later.

To simplify the use of these commands, we'll be using the header from NsDialogs UserData.

!include "nsDialogs.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
OutFile "test.exe"
 
Page Custom pre
 
var dialog
var hwnd
 
Function pre
	nsDialogs::Create 1018
		Pop $dialog
	${NSD_CreateRadioButton} 0 0 40% 6% "Group 1, Radio 1"
		Pop $hwnd
		${NSD_AddStyle} $hwnd ${WS_GROUP}
		${NSD_SetUserData} $hwnd "Group1Radio1"
		${NSD_OnClick} $hwnd RadioClick
	${NSD_CreateRadioButton} 0 12% 40% 6% "Group 1, Radio 2"
		Pop $hwnd
		${NSD_SetUserData} $hwnd "Group1Radio2"
		${NSD_OnClick} $hwnd RadioClick
 
	nsDialogs::Show
FunctionEnd
 
Function RadioClick
	Pop $hwnd
	${NSD_GetUserData} $hwnd $0
	${If} $0 == "Group1Radio1"
	    MessageBox MB_OK "onClick:Group1Radio1"
	${ElseIf} $0 == "Group1Radio2"
	    MessageBox MB_OK "onClick:Group1Radio2"
	${EndIf}
FunctionEnd
 
Section ""
SectionEnd

Using this method, you can easily set a variable to an internal string stored on the radiobutton control without any If-ElseIf-EndIf or Case selections in the OnClick event function at all.

!include "nsDialogs.nsh"
!include "winmessages.nsh"
!include "logiclib.nsh"
OutFile "test.exe"
 
Page Custom pre post
 
var dialog
var hwnd
var minor
 
Function pre
	nsDialogs::Create 1018
		Pop $dialog
 
	${NSD_CreateRadioButton} 0 0 40% 6% "I am 14 years of age or older"
		Pop $hwnd
		${NSD_AddStyle} $hwnd ${WS_GROUP}
		${NSD_SetUserData} $hwnd "false"
		${NSD_OnClick} $hwnd RadioClick
	${NSD_CreateRadioButton} 0 12% 40% 6% "I am younger than 14 of age"
		Pop $hwnd
		${NSD_SetUserData} $hwnd "true"
		${NSD_OnClick} $hwnd RadioClick
 
	nsDialogs::Show
FunctionEnd
 
Function RadioClick
	Pop $hwnd
	${NSD_GetUserData} $hwnd $minor
FunctionEnd
 
Function post
	${If} $minor == ""
	    MessageBox MB_OK "Please specify your age group"
	    Abort
	${ElseIf} $minor == true
	    MessageBox MB_OK "installation will continue with content appropriate for your age"
	${Else}
	    MessageBox MB_OK "installation will continue normally"
	${EndIf}
FunctionEnd
 
Section ""
SectionEnd

How to create a typical UpDown control

The UpDown control is not included with nsDialogs by default, but can easily be created by extending nsDialogs' existing control library.

Typical usage of an UpDown control is in conjunction with a number field, a 'buddy' in Windows controls parlance, created immediately prior to the UpDown control, which integrates the UpDown control and automatically updates as the user clicks the up/down arrows, so the below define will be setup for that use.

!define /math UDM_SETBUDDY   ${WM_USER} + 105
!define /math UDM_SETRANGE32 ${WM_USER} + 111
!define /math UDM_GETRANGE32 ${WM_USER} + 112
!define /math UDM_SETPOS32   ${WM_USER} + 113
!define /math UDM_GETPOS32   ${WM_USER} + 114
 
!define UDS_WRAP                0x0001
!define UDS_SETBUDDYINT         0x0002
!define UDS_ALIGNRIGHT          0x0004
!define UDS_ALIGNLEFT           0x0008
!define UDS_AUTOBUDDY           0x0010
!define UDS_ARROWKEYS           0x0020
!define UDS_HORZ                0x0040
!define UDS_NOTHOUSANDS         0x0080
!define UDS_HOTTRACK            0x0100
 
!define UDN_FIRST -721
!define /math UDN_DELTAPOS ${UDN_FIRST} -
 
!define __NSD_UpDown_CLASS msctls_updown32
!define __NSD_UpDown_STYLE ${DEFAULT_STYLES}|${UDS_SETBUDDYINT}|${UDS_ALIGNRIGHT}|${UDS_AUTOBUDDY}|${UDS_ARROWKEYS|${UDS_NOTHOUSANDS}|${UDS_HOTTRACK}
!define __NSD_UpDown_EXSTYLE 0
 
!insertmacro __NSD_DefineControl UpDown

After including this in your script, you can use ${NSD_CreateUpDown} to create the control.

An example use, presuming you have the above in "nsDialogs_updown.nsh", with the range of the UpDown control limited between 0 and 100, and its current value set to 50.

!include "nsDialogs.nsh"
!include "nsDialogs_createUpdown.nsh"
 
OutFile "$%temp%\temp.exe"
 
var dialog
var hwnd
var null
 
Page custom Test
 
Function Test
  nsDialogs::Create 1018
    Pop $dialog
 
  ${NSD_CreateNumber} 0 0 60u 12u ""
    Pop $null
  ${NSD_CreateUpDown} 0 0 0 0 ""
    Pop $hwnd
    SendMessage $hwnd ${UDM_SETRANGE32} 0 100000 ; min max
    SendMessage $hwnd ${UDM_SETPOS32} 0 50000 ; 0 value
 
  nsDialogs::Show
FunctionEnd
 
Section
SectionEnd

Note that because of the ${UDS_AUTOBUDDY} style, the Number control will automatically be used as the UpDown control's 'buddy' - the control receiving the value of the UpDown control through ${UDS_SETBUDDYINT}. If for any reason whatsoever you cannot create the control that should be the UpDown control's 'buddy' immediately prior to creating the UpDown control, use the following to set the correct 'buddy'.

SendMessage $hwndUpDownControl ${UDM_SETBUDDY} $hwndBuddyControl

Note also that the UpDown control's sizes are all zero. This is because Windows will automatically fit the UpDown arrows within the Number text field - so keep in mind that your Number text field will need some space for the UpDown arrows.

How to create a custom-styled UpDown control

Unfortunately, most of the UpDown control's styles can't be set -after- the UpDown control has been created, meaning you can't use ${NSD_AddStyle} to add the style after the fact, and you'd have to either adjust the defines from the typical UpDown control, or create the control manually through nsDialogs::CreateControl.

For example, you may wish to have left/right arrows rather than up/down arrows - and you may wish those separate from the Number text field as otherwise the left/right arrows appear too small. To do this, the ${UDS_ALIGNRIGHT} style needs to be removed, and the ${UDS_HORZ} style added - and, unlike the previous example, the position and size of the UpDown control should be set directly as it won't be integrated with the Number text field 'buddy' anymore:

Function Test
  nsDialogs::Create 1018
    Pop $dialog
 
  ${NSD_CreateNumber} 0 0 60u 12u ""
    Pop $null
  nsDialogs::CreateControl msctls_updown32 \
    ${DEFAULT_STYLES}|${UDS_SETBUDDYINT}|${UDS_AUTOBUDDY}|${UDS_ARROWKEYS}|${UDS_NOTHOUSANDS}|${UDS_HOTTRACK}|${UDS_HORZ} \
    0 \
    60u 0 20u 12u \
    ""
    Pop $hwnd
    SendMessage $hwnd ${UDM_SETRANGE32} 0 100000 ; min max
    SendMessage $hwnd ${UDM_SETPOS32} 0 50000 ; 0 value
 
  nsDialogs::Show
FunctionEnd

How to detect UpDown control interaction

You can detect an UpDown control interaction using the ${NSD_OnNotify} callback, checking whether the message is ${UDN_DELTAPOS} and then getting the information out of the data packet using the System plugin:

!include "nsDialogs.nsh"
!include "nsDialogs_createUpdown.nsh"
 
OutFile "$%temp%\temp.exe"
 
var dialog
var hwnd
var null
 
var msg
var nmupdown
 
Page custom Test
 
Function Test
  nsDialogs::Create 1018
    Pop $dialog
 
  ${NSD_CreateNumber} 0 0 60u 12u ""
    Pop $null
  ${NSD_CreateUpDown} 0 0 0 0 ""
    Pop $hwnd
    ${NSD_OnNotify} $hwnd updown.onnotify
    SendMessage $hwnd ${UDM_SETRANGE32} 0 100000 ; min max
    SendMessage $hwnd ${UDM_SETPOS32} 0 50000 ; 0 value
 
  nsDialogs::Show
FunctionEnd
 
Function updown.onnotify
  Pop $hwnd
  Pop $msg
  Pop $nmupdown
    ${If} $msg = ${UDN_DELTAPOS}
      System::Call "*$nmupdown(i, i, i, i.r0, i.r1)"
      ; $0 = current value
      ; $1 = value that will be added (may be negative)
      IntOp $2 $0 + $1
      ; $2 = new value
      MessageBox MB_OK "[$0]+[$1]=[$2]"
  ${EndIf}
FunctionEnd
 
Section
SectionEnd

How to Control a Progress Bar control (using absolute values)

You can control a Progress Bar using sendMessage and the messages for Progress Bar controls. These are _almost all_ defined in the WinMessages.nsh header that comes with NSIS, so be sure to include this header.

There is at least one exception, which is very useful to define yourself because it cuts down on code and makes your source more easily readable, and that is PBM_SETRANGE32. PBM_SETRANGE32 allows you to set the start and end range as simply two numbers, instead of having to use ${MakeLong} from WinDef.nsh as required by PBM_SETRANGE

!define /math PBM_SETRANGE32 ${WM_USER} + 6


In order to update the Progress Bar's value, you'll need to use an nsDialogs timer to invoke a function that handles your actual processing. This timer is only invoked once.

Here's a simple example that creates a Progress Bar that has a 0 (zero) to 100 range for a typical full percentage increase, and sets the progress to 25%, 50%, 75% and 100%, using the Sleep command to simulate processing occurring - normally this might be other NSIS calls or calling external applications/installers before your main installation, etc.

!include "WinMessages.nsh"
!include "MUI2.nsh"
!include "nsDialogs.nsh"
 
OutFile "test.exe"
 
Section
SectionEnd
 
Var dialog
Var hwnd
Var null
 
!define /math PBM_SETRANGE32 ${WM_USER} + 6
 
Page Custom page.custom
Function page.custom
    nsDialogs::Create 1018
    Pop $dialog
 
    ${NSD_CreateProgressBar} 0 0 100% 10% "Test"
        Pop $hwnd
 
     ${NSD_CreateTimer} NSD_Timer.Callback 10
 
    nsDialogs::Show
FunctionEnd
 
Function NSD_Timer.Callback
    ${NSD_KillTimer} NSD_Timer.Callback
    SendMessage $hwnd ${PBM_SETRANGE32} 0 100
 
    SendMessage $hwnd ${PBM_SETPOS} 25 0
    Sleep 2000
    SendMessage $hwnd ${PBM_SETPOS} 50 0
    Sleep 2000
    SendMessage $hwnd ${PBM_SETPOS} 75 0
    Sleep 2000
    SendMessage $hwnd ${PBM_SETPOS} 100 0
FunctionEnd
 
!insertmacro MUI_LANGUAGE "English"

How to Control a Progress Bar control (using step values)

One of the down sides of the previous example is that you always have to keep track of your percentages, when those percentages might not even be important, as long as the user knows there -is- progress. An example where keeping track of your percentages could become problematic is with copy/pasting chunks of code. You might suddenly find yourself with a progress bar that goes from 0% to 50% back to 25% and then up to 75%.

One pretty simple solution to this is to use a stepped progress bar instead. This method lets you define a step increment value, and simply call a step function to always increment the progress bar by that value. Here is an example using this method, which is functionally the same as the previous example. Note that we're setting the range to the number of steps we're expecting in our function.

!include "WinMessages.nsh"
!include "MUI2.nsh"
!include "nsDialogs.nsh"
 
OutFile "test.exe"
 
Section
SectionEnd
 
Var dialog
Var hwnd
Var null
 
!define /math PBM_SETRANGE32 ${WM_USER} + 6
 
Page Custom page.custom
Function page.custom
    nsDialogs::Create 1018
    Pop $dialog
 
	${NSD_CreateProgressBar} 0 0 100% 10% "Test"
		Pop $hwnd
 
     ${NSD_CreateTimer} NSD_Timer.Callback 10
 
    nsDialogs::Show
FunctionEnd
 
Function NSD_Timer.Callback
    ${NSD_KillTimer} NSD_Timer.Callback
    SendMessage $hwnd ${PBM_SETRANGE32} 0 4
    SendMessage $hwnd ${PBM_SETSTEP} 1 0
 
    SendMessage $hwnd ${PBM_STEPIT} 0 0
    Sleep 2000
    SendMessage $hwnd ${PBM_STEPIT} 0 0
    Sleep 2000
    SendMessage $hwnd ${PBM_STEPIT} 0 0
    Sleep 2000
    SendMessage $hwnd ${PBM_STEPIT} 0 0
 
    SendMessage $hwnd ${PBM_SETPOS} 4 0
FunctionEnd
 
!insertmacro MUI_LANGUAGE "English"

Note that at the end, we still set the progress to 100%. The reason for this is that you might have some conditional steps in your function. If you forget to step the progress, then the progress value will fall short of 100%. This is an oft-reported user interface issue, where the user complains that even though the process is complete, the progress bar is stuck at less than 100%.

You'll still have to take care to not use -more- steps than you have specified in the PBM_SETRANGE32 call, as any steps after that will just show 100% without any apparent progress.

How to Create a Marquee (endless loop) Progress Bar control

Sometimes you might not know anything about progress - for example, you're simply executing a task that may take some time but there's no way to tell how far along that task you are. In that case you might want to use a so-called Marquee progress bar that simply loops from left to right endlessly.

There is one caveat to this: You can't use a Marquee progress bar unless you use an interface that uses XP styles (see also XPStyle).

Other than that, it's smooth sailing by using ${NSD_AddStyle} to add the PBS_MARQUEE style to the progress bar.

Here's an example of doing exactly that:

!include "WinMessages.nsh"
!include "MUI2.nsh"
!include "nsDialogs.nsh"
 
OutFile "test.exe"
 
Section
SectionEnd
 
Var dialog
Var hwnd
Var null
 
!define PBS_MARQUEE 0x08
 
Page Custom page.custom
Function page.custom
    nsDialogs::Create 1018
    Pop $dialog
 
    ${NSD_CreateProgressBar} 0 0 100% 10% "Test"
        Pop $hwnd
        ${NSD_AddStyle} $hwnd ${PBS_MARQUEE}
 
    ${NSD_CreateTimer} NSD_Timer.Callback 10
    nsDialogs::Show
FunctionEnd
 
Function NSD_Timer.Callback
    ${NSD_KillTimer} NSD_Timer.Callback ; Kill the timer
    SendMessage $hwnd ${PBM_SETMARQUEE} 1 50 ; start=1|stop=0 interval(ms)=+N
    _again:
    MessageBox MB_YESNO "Stop the marquee?" IDNO _again
        SendMessage $hwnd ${PBM_SETMARQUEE} 0 0
 
FunctionEnd
 
!insertmacro MUI_LANGUAGE "English"

Note that a Marquee Progress Bar always runs from left to right - you can't set it to any other position.

How to Create a Smooth Progress Bar control

A Smooth Progress Bar is one that is not cut up into small rectangles - i.e. the entire progress is one solid, smooth, bar.

Unfortunately, there are two caveats in this case:

1. The standard Progress Bar is styled by XP styles which override this style. For the same reason you can't have a Marquee progress bar _without_ XP Styles, you can't have a Smooth progress bar _with_ XP Styles (see also XPStyle).

2. The Smooth style (PBS_SMOOTH) can't be added -after- the control has been created by nsDialogs. So you have to create it with that style already set.

Below is an example that uses a Smooth Progress Bar, with XP Styles off.

!include "WinMessages.nsh"
!include "MUI2.nsh"
!include "nsDialogs.nsh"
 
OutFile "test.exe"
 
Section
SectionEnd
 
Var dialog
Var hwnd
Var null
 
!define PB_EXSTYLE ${WS_EX_WINDOWEDGE}|${WS_EX_CLIENTEDGE}
!define PBS_SMOOTH 0x01
!define /math PBM_SETRANGE32 ${WM_USER} + 6
 
Page Custom page.custom
Function page.custom
    nsDialogs::Create 1018
    Pop $dialog
 
    nsDialogs::CreateControl "msctls_progress32" \
        ${DEFAULT_STYLES}|${PBS_SMOOTH} \
        ${PB_EXSTYLE} \
        0 0 100% 10% \
        "Test" \
 
        Pop $hwnd
 
    ${NSD_CreateTimer} NSD_Timer.Callback 10
    nsDialogs::Show
FunctionEnd
 
Function NSD_Timer.Callback
    ${NSD_KillTimer} NSD_Timer.Callback ; Kill the timer
    SendMessage $hwnd ${PBM_SETRANGE32} 0 100
 
    SendMessage $hwnd ${PBM_SETPOS} 25 0
    Sleep 2000
    SendMessage $hwnd ${PBM_SETPOS} 50 0
    Sleep 2000
    SendMessage $hwnd ${PBM_SETPOS} 75 0
    Sleep 2000
    SendMessage $hwnd ${PBM_SETPOS} 100 0
FunctionEnd
 
!insertmacro MUI_LANGUAGE "English"
XPStyle off

How to let controls receive Hotkey (alt+letter) combinations

Only controls that have labels can be assigned a hotkey, simply by including an ampersand in its title. E.g.

${NSD_CreateCheckbox} 3% 3% 20% 8% "The hot&key here is alt+k"

However, when the page is first navigated to in NSIS, the NSIS dialog is what has focus, rather than the inner nsDialogs dialog. So set focus to the nsDialogs dialog before you tell nsDialogs to show it:

nsDialogs::Create 1018
Pop $dialog ; assuming you have a 'dialog' variable!
; further code here
SendMessage $dialog ${WM_SETFOCUS} $HWNDPARENT 0
nsDialogs::Show

The NSIS dialog will still handle e.g. alt+N for the Next button as the hotkey event simply 'bubbles up' to the parent window if window with current focus does not react to it. ( Hint: That means you shouldn't set any hotkeys for the B(ack) or N(ext) letters! )

Using these methods, you can easily create an installer that can be navigated entirely and easily by keyboard-only.

How to create a sorted droplist control

Sometimes you may have to display a dropdown list with sorted information - for example, a list of folders or files (retrieved through one of the many filesearch scripts) that may not have come in sorted.

One solution would be to pre-sort the results using a sorting script or the Array plugin.

Another solution is to use a dropdown list which has a 'sorted' style, CBS_SORT, set on it so that its listings are automatically sorted.

Unfortunately this is not a style you can not add after the control has been created, so you have to create it yourself.

Below is a header file I wrote for use in our installer which lets us use the same syntax as nsDialogs uses for other controls.

/*
nsDialogs_createDroplistSorted.nsh
Header file for creating a sorted droplist control.
 
Usage:
  ${NSD_CreateDroplistSorted} left top width height ""
  Creates the sorted droplist at the location specified.
*/
 
!ifndef NSDIALOGS_createDroplistSorted_INCLUDED
	!define NSDIALOGS_createDroplistSorted_INCLUDED
	!verbose push
	!verbose 3
 
	!include WinMessages.nsh
 
	!define __NSD_DropListSorted_CLASS COMBOBOX
	!define __NSD_DropListSorted_STYLE ${DEFAULT_STYLES}|${WS_TABSTOP}|${WS_VSCROLL}|${WS_CLIPCHILDREN}|${CBS_AUTOHSCROLL}|${CBS_HASSTRINGS}|${CBS_DROPDOWNLIST}|${CBS_SORT}
	!define __NSD_DropListSorted_EXSTYLE ${WS_EX_WINDOWEDGE}|${WS_EX_CLIENTEDGE}
 
	!insertmacro __NSD_DefineControl DropListSorted
 
	!verbose pop
!endif