May 25, 2022, 04:02:00 PM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


MessageBox replacement

Started by fasecero, September 19, 2019, 07:16:28 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

fasecero

A task dialog is similar to, while much more flexible than, a basic message box.

A task dialog contains application-defined message text and title, icons, any combination of predefined push buttons, additional and footer messages, verification check box, command links, and hyperlinks. You can kinda guide the user in more detail through the decision he/she has to make.

The code uses some programming stuff: dynamic link libraries (calling a system function dynamically), function callback (custom message processing), dynamic variables (to bypass the requirement of 'fixed' arrays), and referencing and dereferencing pointers to those dynamic variables. Anyway, the resulting functions are very easy to use.

The following is an over-exaggerated illustration of a: yes/no/cancel messagebox: "Do you want to save the changes before exiting?"

Requirement: Windows Vista+, Windows Styles enabled (you need to use a .manifest file)

$INCLUDE "windowssdk.inc"
$INCLUDE "commctrl.inc"
$INCLUDE "Uxtheme.inc"

$USE "Uxtheme.lib"

' -------------------------------------------------------------------------------------------------------------------------------------------------------
' TASK DIALOG CUSTOM STRUCT
' -------------------------------------------------------------------------------------------------------------------------------------------------------

TYPE _TASKDIALOG
TASKDIALOGCONFIG config
pointer textbt
pointer bt
INT nButtonPressed
BOOL pfVerificationFlagChecked
ENDTYPE

' -------------------------------------------------------------------------------------------------------------------------------------------------------
' MAIN LOOP
' -------------------------------------------------------------------------------------------------------------------------------------------------------

OleInitialize(0) ' THIS IS MANDATORY + VISUAL STYLES ENABLED
ShowTaskDialog()
OleUninitialize()
END

' -------------------------------------------------------------------------------------------------------------------------------------------------------
' TASK DIALOG EXAMPLE
' -------------------------------------------------------------------------------------------------------------------------------------------------------

SUB ShowTaskDialog()
_TASKDIALOG task
string url = "<A HREF=\"http://www.ionicwind.com/forums/index.php\">Ionic Wind</A>" ' ' standard hyperlink format

' INIT THE TASK DIALOG
' arguments: parent HWND
TaskDialogInit(task, 0)

/* BEGIN OF TASK DIALOG SECTIONS */
/* comment any section to skip it */

' HEADER INFO
' arguments: caption, title, description, icon: TD_ERROR_ICON, TD_WARNING_ICON, TD_INFORMATION_ICON, TD_SHIELD_ICON, OPT custom HICON
TaskDialogSetHeader(task, L"Confirmation", L"What do you want to do with your current progress?", L"", TD_INFORMATION_ICON, 0)

' USER COMMAND BUTTONS
' arguments: number of buttons, default button ID
TaskDialogSetCommandLinks(task, 3, IDCANCEL)
' arguments: position, button ID, title, description
TaskDialogCommandSetInfo(task, 1, 100, "Exit and save my changes", "Save you progress, then exit. This will overwrite any previosely saved project.")
TaskDialogCommandSetInfo(task, 2, 101, "Exit and don't save", "Exit without saving the file. This will discard any change you did to the file.")
TaskDialogCommandSetInfo(task, 3, 102, "Don't exit!", "Return to your current file.")

' SYSTEM COMMAND BUTTONS
'arguments: buttons: TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON
TaskDialogSetSystemButtons(task, TDCBF_CANCEL_BUTTON)

' CUSTOM CHECKBOX
' arguments: text, checked
TaskDialogSetCheckbox(task, L"Don't ask me again / autosave next time", FALSE)

' EXPANDABLE ADITIONAL INFO
' arguments: expanded, text, expand button text, collapse button text
TaskDialogSetExpandableInfo(task, FALSE, L"Note: If you need more information please visit "+ S2W(url) + L" forum.", L"View details", L"Hide details")

' FOOTER INFO
' arguments: text, icon: TD_ERROR_ICON, TD_WARNING_ICON, TD_INFORMATION_ICON, TD_SHIELD_ICON, OPT custom HICON
TaskDialogSetFooter(task, L"If you don't choose to save your changes will be lost.", TD_INFORMATION_ICON, 0)

/* comment any section to skip it */
/* END OF TASK DIALOG SECTIONS */

' SHOW THE TASK DIALOG ON THE SCREEN
ShowTask(task)

' RELEASE TASK DIALOG DATA (call it every time you finish with one taskdialog and before using TaskDialogInit again )
TaskDialogFinish(task)

' DISPLAY THE USER'S CHOICE (you can also get checkbox's value with task.pfVerificationFlagChecked)
SELECT (task.nButtonPressed)
CASE IDCANCEL
_MessageBox(0, "The user has cancelled the task dialog", "", 0)
DEFAULT
_MessageBox(0, "The user has selected the button ID: " + STR$(task.nButtonPressed), "", 0)
ENDSELECT
ENDSUB

' -------------------------------------------------------------------------------------------------------------------------------------------------------
' TASK DIALOG FUNCTIONS
' -------------------------------------------------------------------------------------------------------------------------------------------------------

SUB TaskDialogInit(_TASKDIALOG task, HWND parent)
ZeroMemory(&task, LEN(task))

task.config.hwndParent = parent
task.config.cbSize                       = LEN(task.config)
task.config.hInstance                    = GetModuleHandle(0)

task.config.dwFlags = task.config.dwFlags | TDF_ENABLE_HYPERLINKS
ENDSUB

' sysbuttons: TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON
SUB TaskDialogSetSystemButtons(_TASKDIALOG task, INT sysbuttons)
task.config.dwCommonButtons = sysbuttons
ENDSUB

' headerIcon (only 1 value): TD_ERROR_ICON, TD_WARNING_ICON, TD_INFORMATION_ICON, TD_SHIELD_ICON
SUB TaskDialogSetHeader(_TASKDIALOG task, wstring caption, wstring title, wstring description, INT headerIcon, OPT INT customIcon = 0)
task.config.pszWindowTitle               = caption
task.config.pszMainInstruction           = title
task.config.pszContent                   = description

IF customIcon THEN
task.config.hMainIcon = customIcon
task.config.dwFlags = task.config.dwFlags | TDF_USE_HICON_MAIN
task.config.pszMainIcon = 0
ELSE
IF headerIcon THEN
task.config.pszMainIcon = MAKEINTRESOURCE(headerIcon)
ELSE
task.config.pszMainIcon = 0
ENDIF
ENDIF
ENDSUB

TYPE myString
wstring text
ENDTYPE

SUB TaskDialogSetCommandLinks(_TASKDIALOG task, INT numberOfCommands, INT defaultID)
pointer textbt = NEW(myString, numberOfCommands)
pointer bt = NEW(TASKDIALOG_BUTTON, numberOfCommands)

task.textbt = textbt + 0
task.bt = bt + 0
task.config.dwFlags                     = task.config.dwFlags | TDF_USE_COMMAND_LINKS
task.config.cButtons                     = numberOfCommands
task.config.pButtons                     = task.bt + 0
task.config.nDefaultButton             = defaultID
ENDSUB

SUB TaskDialogCommandSetInfo(_TASKDIALOG task, INT pos, INT id, string title, string description)
pointer textbt = task.textbt + 0
pointer bt = task.bt + 0

#<myString>textbt[pos - 1].text = GenerateUnicodeCommandLinkText(title, description)
#<TASKDIALOG_BUTTON>bt[pos - 1].pszButtonText = #<myString>textbt[pos - 1].text
#<TASKDIALOG_BUTTON>bt[pos - 1].nButtonID = id
ENDSUB

SUB TaskDialogFinish(_TASKDIALOG task)
pointer textbt = task.textbt + 0
pointer bt = task.bt + 0

IF textbt THEN DELETE(textbt)
IF bt THEN DELETE(bt)
ENDSUB

SUB TaskDialogSetCheckbox(_TASKDIALOG task, wstring text, INT checked)
task.config.pszVerificationText = text
IF checked THEN task.config.dwFlags = task.config.dwFlags | TDF_VERIFICATION_FLAG_CHECKED
ENDSUB

SUB TaskDialogSetExpandableInfo(_TASKDIALOG task, INT expanded, wstring info, wstring expandText, wstring collapseText)
task.config.pszExpandedInformation     = info
task.config.pszExpandedControlText     = collapseText
task.config.pszCollapsedControlText     = expandText

IF expanded THEN task.config.dwFlags = task.config.dwFlags | TDF_EXPANDED_BY_DEFAULT
task.config.dwFlags = task.config.dwFlags | TDF_EXPAND_FOOTER_AREA
ENDSUB

' headerIcon (only 1 value): TD_ERROR_ICON, TD_WARNING_ICON, TD_INFORMATION_ICON, TD_SHIELD_ICON
SUB TaskDialogSetFooter(_TASKDIALOG task, wstring text, INT footerIcon, OPT INT customIcon = 0)
task.config.pszFooter               = text

IF customIcon THEN
task.config.hFooterIcon = customIcon
task.config.dwFlags = task.config.dwFlags | TDF_USE_HICON_FOOTER
task.config.pszFooterIcon = 0
ELSE
IF footerIcon THEN
task.config.pszFooterIcon = MAKEINTRESOURCE(footerIcon)
ELSE
task.config.pszFooterIcon = 0
ENDIF
ENDIF
ENDSUB

SUB ShowTask(_TASKDIALOG task), INT
task.config.pfCallback = &Pftaskdialogcallback
_TaskDialogIndirect(&task.config, &task.nButtonPressed, NULL, &task.pfVerificationFlagChecked)
RETURN task.nButtonPressed
ENDSUB

' TASK DIALOG NOTIFICATIONS
' TDN_BUTTON_CLICKED, TDN_CREATED, TDN_DESTROYED, TDN_DIALOG_CONSTRUCTED, TDN_EXPANDO_BUTTON_CLICKED
' TDN_HELP, TDN_HYPERLINK_CLICKED, TDN_NAVIGATED, TDN_RADIO_BUTTON_CLICKED, TDN_TIMER, TDN_VERIFICATION_CLICKED
SUB Pftaskdialogcallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData), HRESULT
HRESULT hr = S_OK

SELECT msg
CASE TDN_CREATED
'setMainThemeAttributes(hwnd)
CASE TDN_HYPERLINK_CLICKED
SYSTEM(W2S(*<wstring>lParam))
ENDSELECT

RETURN hr
ENDSUB

SUB GenerateUnicodeCommandLinkText(string title, string text), WSTRING
WSTRING buffer = S2W(title) + L"|" + S2W(text) + WCHR$(10) + L" "

INT pos = WINSTR(buffer, L"|") - 1

IF pos THEN
pointer p = &buffer[pos] + 0
*<char>p = 10 : p+=1 : *<char>p = 0
ENDIF

RETURN buffer
ENDSUB

DECLARE TaskDialogIndirectTemplate(pointer pTaskConfig, pointer pnButton, pointer pnRadioButton, pointer pfVerificationFlagChecked), HRESULT

SUB _TaskDialogIndirect(pointer pTaskConfig, pointer pnButton, pointer pnRadioButton, pointer pfVerificationFlagChecked), HRESULT
HRESULT value = 0
UINT hInst=LoadLibraryW(L"ComCtl32.dll")

IF hInst THEN
UINT proc = GetProcAddress(hInst, "TaskDialogIndirect")

IF proc THEN
value = !<TaskDialogIndirectTemplate>proc(pTaskConfig, pnButton, pnRadioButton, pfVerificationFlagChecked)
ENDIF

FreeLibrary(hInst)
ENDIF

RETURN value
ENDSUB

SUB setMainThemeAttributes(int hwnd)
WTA_OPTIONS options

options.dwFlags = WTNCA_NODRAWCAPTION | WTNCA_NODRAWICON
options.dwMask = WTNCA_VALIDBITS

' The SetWindowThemeAttribute API call takes care of everything
SetWindowThemeAttribute(hwnd, WTA_NONCLIENT, options, LEN(options))
ENDSUB


LarryMc

LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library