With in Graphical Installer & NSIS, the text of Label overlapped when I changed it dynamically - label

I created a custom page which contains a label and two buttons to control the label's contents.
I suppose to set the text of the label within callbacks of the buttons, as the following codes:
Function FuncShowNewFeature
; ...
; Add Controls
${NSD_CreateLabel} 0 13u 100% 12u "Show new features here"
Pop $lblContents
${GraphicalInstallerRedrawLabel} $lblContents
${NSD_CreateButton} 0 -13u 40u 13u "Prev"
Pop $btnPrev
${NSD_OnClick} $btnPrev PrevFeature ; callback
${NSD_CreateButton} -40u -13u 40u 13u "Next"
Pop $btnNext
${NSD_OnClick} $btnNext NextFeature ; callback
# Put this macro before EACH calling nsDialogs::Show to draw background properly
${GraphicalInstallerRedrawnsDialogsPage}
nsDialogs::Show
FunctionEnd
Function NextFeature
${NSD_SetText} $lblContents "To show the next tip of new-feature"
${GraphicalInstallerRedrawLabel} $lblContents ;I don't know whether this macro is necessary here
FunctionEnd
Function PrevFeature
${NSD_SetText} $lblContents "To show the previous tip of new-feature"
${GraphicalInstallerRedrawLabel} $lblContents ;I don't know whether this macro is necessary here
FunctionEnd
But the result shows something wrong, which the "new" text overlapped on the old ones, just like the label has not been refreshed/cleared before redrawing.
Did I miss any necessary calling in my process?

You are missing GraphicalInstaller::SubclassLabel /NOUNLOAD $variable_name
The correct code:
Function FuncShowNewFeature
; ...
; Add Controls
${NSD_CreateLabel} 0 13u 100% 12u "Show new features here"
Pop $lblContents
${GraphicalInstallerRedrawLabel} $lblContents
GraphicalInstaller::SubclassLabel /NOUNLOAD $lblContents # <<< ADDED HERE
${NSD_CreateButton} 0 -13u 40u 13u "Prev"
Pop $btnPrev
${NSD_OnClick} $btnPrev PrevFeature ; callback
${NSD_CreateButton} -40u -13u 40u 13u "Next"
Pop $btnNext
${NSD_OnClick} $btnNext NextFeature ; callback
# Put this macro before EACH calling nsDialogs::Show to draw background properly
${GraphicalInstallerRedrawnsDialogsPage}
nsDialogs::Show
FunctionEnd
Function NextFeature
${NSD_SetText} $lblContents "To show the next tip of new-feature"
FunctionEnd
Function PrevFeature
${NSD_SetText} $lblContents "To show the previous tip of new-feature"
FunctionEnd
GraphicalInstaller::Subclass[CONTROL] is part of
${GraphicalInstallerRedraw[CONTROL]} macro.
This works fine for [CONTROL] of type RadioButton or CheckBox, but it is missing in Label.
We will fix this in next release, sorry for this inconsistency.

One simple workaround is to just hide the label while you update its text:
!include nsDialogs.nsh
Page Custom gfxpage
Function onClick
Pop $0
System::Call 'kernel32::GetTickCount()i.r0' ; "Arbitrary" number
ShowWindow $2 0
${NSD_SetText} $2 "$0$0$0"
ShowWindow $2 1
FunctionEnd
Function .onInit
InitPluginsDir
File /oname=$PLUGINSDIR\image.bmp "${NSISDIR}\Contrib\Graphics\Header\nsis-r.bmp"
FunctionEnd
Function gfxpage
nsDialogs::Create 1018
Pop $0
${NSD_CreateButton} 0 -13u 100% 12u "Click me"
Pop $1
${NSD_OnClick} $1 onClick
${NSD_CreateLabel} 0 0 100% 12u "Hello, welcome to nsDialogs!"
Pop $2
SetCtlColors $2 000000 transparent
${NSD_CreateBitmap} 0 0 100% 12u ""
Pop $3
${NSD_SetBitmap} $3 $PLUGINSDIR\image.bmp $4
nsDialogs::Show
${NSD_FreeBitmap} $4
FunctionEnd

Related

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?
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

NSIS: Custom page on StateChanged

I have a some different license pages with check box on it. The Next button should be disabled if check box is unchecked. Is there an event that I can use if checkbox state changed? Here is my code of one of this
var Window
var labelDescription
var checkBoxIsUserAgree
Function CreateCustomLicense1
nsDialogs::Create 1018
Pop $Window
GetDlgItem $0 $HWNDPARENT
EnableWindow $0 0
${NSD_CreateLabel} 13u 22u 270u 96u "Description"
Pop $labelDescription
${NSD_CreateCheckBox} 10u 110u 100u 15u "I Agree"
$checkBoxIsUserAgree
FunctionEnd
Function ShowCustomLicence1
Call CreateCustomLicense1
nsDialogs::Show
Function
Function .oncheckBoxIsUserAgreeStateChanged ; what event I can use for track checkbox state changing
EnableWindow $0 1
FunctionEnd
The built-in NSIS license page supports a checkbox and can be used multiple times but if you insist on creating a custom page you just have to add a on* handler:
!include nsDialogs.nsh
Var checkBoxIsUserAgree
Function ShowCustomLicence1
nsDialogs::Create 1018
Pop $1
GetDlgItem $0 $HWNDPARENT 1
EnableWindow $0 0
${NSD_CreateLabel} 13u 22u 270u 90u "Description"
Pop $1
${NSD_CreateCheckBox} 10u 110u 100u 15u "I Agree"
Pop $checkBoxIsUserAgree
${NSD_OnClick} $checkBoxIsUserAgree oncheckBoxIsUserAgreeStateChanged1
nsDialogs::Show
FunctionEnd
Function oncheckBoxIsUserAgreeStateChanged1
Pop $1 ; Throw away parameter
${NSD_GetState} $checkBoxIsUserAgree $1
EnableWindow $0 $1
FunctionEnd
Page Custom ShowCustomLicence1
Page InstFiles

How to change custom page label text on the fly in nsis?

I have written a custom page where i want to change the label text on the fly .I tried following code but some how I could not bale to change the text .
Function Maintainance
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
${NSD_CreateLabel} 0 0 100% 12u "The $CurrentVersion complete installation folder is available at the below link"
Pop $Label
FindWindow $0 "#32770" "" $HWNDPARENT
GetDlgItem $1 $0 1006
SendMessage $1 ${WM_SETTEXT} 0 "STR:new value 111111"
nsDialogs::Show
FunctionEnd
Any pointer on this will be a help.
The custom page is not visible until you call nsDialogs::Show. Any dynamic action has to happen after that with a timer or in response to a user action:
Page Custom MyPageCreate
Page InstFiles
!include nsDialogs.nsh
var mylabel
Function MyPageCreate
nsDialogs::Create 1018
Pop $0
${NSD_CreateLabel} 0 0 100% 12u "Hello"
Pop $mylabel
${NSD_CreateButton} 0 50% 50% 12u "Change"
Pop $0
${NSD_OnClick} $0 ChangeIt
nsDialogs::Show
FunctionEnd
Function ChangeIt
System::Call kernel32::GetTickCount()i.r0
SendMessage $mylabel ${WM_SETTEXT} 0 "STR:World! $0"
FunctionEnd

Can't find IDOK (1) in the custom UI! How to add a custom OK Button in NSIS?

Please Help ! In NSIS, I'm using
!define MUI_PAGE_CUSTOMFUNCTION_SHOW LicenseShow
then
Function LicenseShow
; New dialog for custom items
nsDialogs::Create 1018
Pop $0
; Accept button
${NSD_CreateButton} 373, 223, 50, 14 "Accept" ; Can't find IDOK (1) in the custom UI!
; Decline button
${NSD_CreateButton} 21, 223, 50, 14 "Decline"
; Picture
${NSD_CreateBitmap} 5 5 100% 100 "disclosure.bmp"
Pop $0
${NSD_SetImage} $0 $PLUGINSDIR\image.bmp $ImageHandle
; Disclaimer
nsDialogs::CreateControl /NOUNLOAD ${__NSD_Text_CLASS} ${DEFAULT_STYLES}|${WS_TABSTOP}|${ES_WANTRETURN}|${ES_MULTILINE} ${__NSD_Text_EXSTYLE} 5 220 660 115 ''
Pop $1
SendMessage $1 ${EM_SETREADONLY} 1 0
SetCtlColors $1 0x000000 0xFFFFFF
${NSD_SetText} $1 "The license text"
nsDialogs::Show
${NSD_FreeImage} $ImageHandle
FunctionEnd
How do I tell that the Accept button has the IDOK on it ?
Please help.
You can set the id by calling System::Call 'user32::SetWindowLong(i $myhwnd,i -12,i $mynewid)' but what you really should be doing is setting up callback functions:
...
${NSD_CreateButton} ...
pop $0
${NSD_OnClick} $0 userclicked
...
Function userclicked
MessageBox mb_ok Hello
SendMessage $hwndparent ${WM_COMMAND} 1 0
FunctionEnd

In NSIS, How to set the size of the dialog box when using nsDialogs::Create 1018?

In Nsis, I'm using:
...
nsDialogs::Create 1018
Pop $0
nsDialogs::Show
...
But the size of the dialog box doesn't fit my needs. How can I specify a length for x and y to this dialog ?
If you want to resize everything it is probably better to use Resource Hacker and ChangeUI but you can do it at runtime:
!include nsDialogs.nsh
Function mypage
System::Call 'user32::SetWindowPos(i$hwndparent,i,i,i,i 640,i 480,i 0x16)' ; Resize outer dialog
nsDialogs::Create 1018
Pop $0
System::Call 'user32::MoveWindow(i$0,i0,i0,i 600,i 200,i0)' ; Resize inner (nsDialogs) page
${NSD_CreateLabel} 0 10u 100% 10u "Hello, welcome to nsDialogs!"
Pop $0
SetCtlColors $0 0xffffff 0xff2255
nsDialogs::Show
FunctionEnd
page custom mypage
My dialog isn't tall enough for all the controls I am putting in.
I tried your two Windows API's and while they worked, the client area of the install overlapped and covered up the OK/Cancel buttons.
I eventually worked out "use resource hacker and ChangeUI". It was much harder than I thought it would be. So, here is a more detailed how-to. I was deeply entrenched with nsDialogs NOT ModernUI. So this is a how-to for nsDialogs resizing of a window created the same as in the examples. ModernUI is covered above.
nsDialogs::Create 1018
Get ResourceHacker from Angus: http://www.angusj.com/resourcehacker/
Go to your NSIS Contrib folder. C:\Program Files (x86)\NSIS\Contrib\UIs and copy default.exe to the same folder as your NSI script file.
Rename your local copy of default.exe to tall_UI.exe.
Open ResourceHacker, drag tall_UI.exe into the window.
Use the treeView to dig down into resource 105 and click on 1033. When you click on 1033, it shows a preview.
What you have a is the code and the preview. Take note of the first line of code...
105 DIALOGEX 0, 0, 280, 162
Now click on the top edge of the preview window and stretch it to make it taller. Note that the final number in that line got bigger. Notice how there is empty space at the bottom of the window now.
There are four controls along the bottom edge of the dialog: a button, an invisible box and two more buttons. Drag all of these down near the bottom of the window. As you select each you will notice the red * marking which control is being modified in the code window. Just get them close to where you want them.
Move the horizontal divider line. It's skinny and hard to move.
You will want your buttons to all be in a straight row. To get them exact, move over to the code window and edit the 3rd number from the end of the line for each of the controls. To apply these changes to the code window, click "Compile Script".
Click on the big gray box to select then use its bottom edge pip to stretch it to the desired height.
Tweak the code or preview. Hit compile a lot.
It's perfect, you like it. Hit compile one final time.
Click "File" and "Save". It will save your Tall_UI.exe as well as make a copy called Tall_UI_original.exe.
In your NSIS script, you need to add a call to ChangeAll early on.
ChangeUI all tall_UI.exe
Page custom nsDialogsPage
Function nsDialogsPage
nsDialogs::Create 1018
Pop $Dialog
...
That did it for me. You will do some trial and error, always hit commpile and save in ResourceHacker and then rebuild your NSI. You may note that your dialog is bigger or smaller than the preview shown in ResourceHacker. That is because NSIS does scale your dialog based on font size, DPI... stuff like that. Try and retry till it looks good.
You will note that nsDialogs::Create 1018 matches the number in the 5th line of resource hacker:
CONTROL "", 1018, STATIC, SS_BLACKRECT | WS_CHILD | WS_GROUP, 7, 7, 266, 160
I did some testing after getting this demo together and the positioning and size of that 1018 resource does have an effect but I can't tell you why it isn't black.
Full code of my demo is below showing.
#Created with NSIS version 2.46 downloaded from SourceForge.net
#Based on "Adding Controls" section of user docs
# http://nsis.sourceforge.net/Docs/nsDialogs/Readme.html#step-add
!include nsDialogs.nsh
Name "Launchpad"
OutFile "Master Installer.exe"
BrandingText " "
Caption "Launchpad"
RequestExecutionLevel admin
SetFont "Arial" 10
VIProductVersion "2.5.0.0"
Var Dialog
Var Button
ChangeUI all tall_UI.exe
Page custom nsDialogsPage
Function nsDialogsPage
nsDialogs::Create 1018
Pop $Dialog
# It will create a new dialog in the page and return its HWND on the stack. The result must be popped from the stack to prevent stack corruption. If the result is error, the dialog couldn't be created.
${If} $Dialog == error
Abort
${EndIf}
# ${NSD_Create*} x y width height text
## Going to use $0 for y of each new control.
StrCpy $0 "29"
${NSD_CreateButton} 50% "$1u" 25% 12u "Product Manual"
Pop $Button
${NSD_OnClick} $Button Manual_Install_Clicked
IntOp $0 $0 + 18
IntOp $1 $0 - 2
${NSD_CreateButton} 50% "$1u" 25% 12u "Product 1 Installer"
Pop $Button
${NSD_OnClick} $Button Product1_Install_Clicked
IntOp $0 $0 + 18
IntOp $1 $0 - 2
${NSD_CreateButton} 50% "$1u" 25% 12u "Product 2 Installer"
Pop $Button
## ${NSD_OnClick} ...
IntOp $0 $0 + 18
IntOp $1 $0 - 2
${NSD_CreateButton} 50% "$1u" 25% 12u "Product 3 Installer"
Pop $Button
## ${NSD_OnClick} ...
IntOp $0 $0 + 18
IntOp $1 $0 - 2
${NSD_CreateButton} 50% "$1u" 25% 12u "Product 4 Installer"
Pop $Button
## ${NSD_OnClick} ...
IntOp $0 $0 + 18
IntOp $1 $0 - 2
${NSD_CreateButton} 50% "$1u" 25% 12u "Product 1 Installer"
Pop $Button
## ${NSD_OnClick} ...
IntOp $0 $0 + 18
IntOp $1 $0 - 2
${NSD_CreateButton} 50% "$1u" 25% 12u "Product 1 Installer"
Pop $Button
## ${NSD_OnClick} ...
IntOp $0 $0 + 18
IntOp $1 $0 - 2
nsDialogs::Show
FunctionEnd
Function ExecInstall
pop $0
ExecWait $0 $1
IfErrors 0 ExecDone
MessageBox MB_OK|MB_IconExclamation "$1 $0 not found"
ExecDone:
##Call Update_Install_Statuses
FunctionEnd
Function Manual_Install_Clicked
ExecShell "open" "$EXEDIR\Manual\Manual.PDF"
FunctionEnd
Function Product1_Install_Clicked
Exec "Explorer.exe $EXEDIR\Support Files"
FunctionEnd
Function Product2_Install_Clicked
Push "$EXEDIR\Product2 Folder\Product2 Installer.exe"
Call ExecInstall
FunctionEnd
Section
SectionEnd

Resources