Moving a TaskDialog
Posted: 18 Mar 2022, 08:35
Had trouble with part of a TaskDialog going offscreen below the taskbar when the "See Details" button was clicked. Turns out it can be done through the callback, so thought it would be an idea to post the code here in case anyone else has the same issue.
The code creates a test form and a test TaskDialog with an expando ("See Details" ) button. It captures the height of the expanded form (hopefully) before it gets to WM_PAINT, thereby adjusting the vertical co-ordinates of the TaskDialog:The script only just does the job, so welcome other better methods. One issue was a hard time attempting to modify the tdYpos element in hWndObj in TDNTimer()- it must be the object is only passed through as a reference then- no problem at all with the other object created within the function.
Edit: It came as to why- it's bound to MoveTDN, so cannot be written to from anywhere else.
Still learning.
Edit: On slower rigs, timerVal has to be set at multiples of its current setting for the same effect, thus it must vary from system to system.
The code creates a test form and a test TaskDialog with an expando ("See Details" ) button. It captures the height of the expanded form (hopefully) before it gets to WM_PAINT, thereby adjusting the vertical co-ordinates of the TaskDialog:
Code: Select all
Gui, testGui: New, -DPIScale -MaximizeBox -MinimizeBox +OwnDialogs +LastFound
Global hwndParent, testGuiH
testGuiW := floor(A_ScreenWidth/6)
testGuiH := floor(A_ScreenHeight/9)
Gui, testGui: Add, Button, center Default w%testGuiW% h%testGuiH% gRunTaskDialog vTaskDlg, Run TaskDialog
Gui, testGui: Default
GuiControl, testGui: Move, TaskDlg, x%testGuiW% y%testGuiH%
testGuiW := floor(A_ScreenWidth/2)
testGuiH := floor(A_ScreenHeight/3)
Gui, testGui: Show, % "y" . A_ScreenHeight - testGuiH . " w" . testGuiW . " h" . testGuiH
Return
RunTaskDialog:
retVal := TaskDialog("Test", "Test Issue", "", "Lorem ipsum dolor sit amet,`nconsectetur adipiscing elit,`nsed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.`nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,`nsunt in culpa qui officia deserunt mollit anim id est laborum.", , "Close Test")
Return
Esc::
testGuiGuiClose:
ExitApp
; Task dialog
TaskDialog(pageTitle := "Page Title", instructionTitle := "Description of issue", description := "Choose one of the following options:", expandedText := "More Information with a <A HREF=""http://www.some_link.com"">Link</a>", checkText := "Do not show this again", choice1 := "", choice2 := "", choice3 := "", choice4 := "")
{
; This function requires A_Unicode and Vista or later.
Static FooterText := ""
; Error Flags
Static S_OK = 0x0, E_OUTOFMEMORY = 0x8007000E, E_INVALIDARG = 0x80070057, E_FAIL = 0x80004005, E_ACCESSDENIED = 0x80070005
;General Flags
Static flags = 0x1011, TDF_VERIFICATION_FLAG_CHECKED = 0x0100, TDF_CALLBACK_TIMER := 0X0800
; 0x1 : TDF_ENABLE_HYPERLINKS
; 0X0010: TDF_USE_COMMAND_LINKS
; 0x1000: TDF_POSITION_RELATIVE_TO_WINDOW
; 0x1000000: TDF_SIZE_TO_CONTENT
CustomButtons := []
Global hwndParent := WinExist("A")
if (!(TDCallback := RegisterCallback("TDCallback", "Fast")))
{
MsgBox, 8208, Task Dialog, Could not Register Callback for the Task dialog.
return 0
}
While (tp := choice%A_Index%)
CustomButtons.Push(100 + A_Index, tp)
cButtons := CustomButtons.Length()/2
VarSetCapacity(pButtons, 4 * cButtons + A_PtrSize * cButtons, 0)
loop %cButtons%
{
iButtonID := CustomButtons[2 * A_Index -1]
iButtonText := &(b%A_Index% := CustomButtons[2 * A_Index])
NumPut(iButtonID, pButtons, (4 + A_PtrSize) * (A_Index - 1), "Int")
NumPut(iButtonText, pButtons, (4 + A_PtrSize) * A_Index - A_PtrSize, "Ptr")
}
; TASKDIALOGCONFIG structure
if (A_PtrSize == 8) ; X64
{
NumPut(VarSetCapacity(TDC, 160, 0), TDC, 0, "UInt") ; cbSize
NumPut(hwndParent, TDC, 4, "Ptr") ; hwndParent
; HINSTANCE
NumPut(flags, TDC, 20, "Int") ; dwflags
NumPut(&pageTitle, TDC, 28, "Ptr") ; pszWindowTitle
NumPut(&instructionTitle, TDC, 44, "Ptr") ; pszMainInstruction
NumPut(&description, TDC, 52, "Ptr") ; pszContent
NumPut(cButtons, TDC, 60, "UInt") ; cButtons
NumPut(&pButtons, TDC, 64, "Ptr") ; pButtons
NumPut(&checkText, TDC, 92, "Ptr") ; pszVerificationText
NumPut(&ExpandedText, TDC, 100, "Ptr") ; pszExpandedInformation
NumPut(&FooterText, TDC, 132, "Ptr") ; pszFooter
NumPut(TDCallback, TDC, 140, "Ptr") ; pfCallback
}
else
return
retVal := DllCall("Comctl32.dll\TaskDialogIndirect", "Ptr", &TDC
, "Int*", Button := 0
, "Int*", Radio := 0
, "Int*", Checked := 0)
; various error checks omitted for brevity
return retVal
}
TDCallback(hWnd, Notification, wParam, lParam, RefData)
{
Static tdYpos := 0
Static TDN_CREATED := 0, TDN_HYPERLINK_CLICKED := 3, TDN_EXPANDO_BUTTON_CLICKED := 10
switch (Notification)
{
Case TDN_CREATED:
{
VarSetCapacity(rect, 16, 0)
if (DllCall("GetWindowRect", "Ptr", hWnd, "Ptr", &rect))
tdYpos := NumGet(rect, 4, "int")
VarSetCapacity(rect, 0)
}
Case TDN_HYPERLINK_CLICKED:
{
url := StrGet(lParam, "UTF-16") ; <A HREF="URL">Link</A>
Run %url%
}
Case TDN_EXPANDO_BUTTON_CLICKED:
{
if (wParam)
tdYposOut := 0
else
tdYposOut := tdYpos
hWndObj := {hWnd:hWnd, tdYpos:tdYposOut}
MoveTDN(hWndObj)
hWndObj := ""
}
Default:
}
}
MoveTDN(hWndObj)
{
timerVal := -20
Timer := Func("TDNTimer").Bind(A_ThisFunc, hWndObj)
SetTimer % Timer, %timerVal%
Return
}
TDNTimer(FuncName, hWndObj)
{
VarSetCapacity(rect, 16, 0)
if (!DllCall("GetWindowRect", "Ptr", hWndObj.hWnd, "Ptr", &rect))
return
tdxpos := NumGet(rect, 0, "int")
if (hWndObj.tdYpos)
hWndNewObj := hWndObj
else
{
WinGetPos,,,, h, ahk_class Shell_TrayWnd
if (h > A_ScreenHeight - 100) ; vert taskbar
h := 0
tdYposIn := A_ScreenHeight - h - (NumGet(rect, 12, "int") - NumGet(rect, 4, "int"))
hWndNewObj := {hWnd:hWndObj.hWnd, tdYpos:tdYposIn}
}
VarSetCapacity(rect, 0)
DllCall("SetWindowPos", "uint", hWndNewObj.hWnd, "uint", hwnd_prev
, "int", tdxpos, "int", hWndNewObj.tdYpos, "int", 0, "int", 0, "uint", 0)
}
Edit: It came as to why- it's bound to MoveTDN, so cannot be written to from anywhere else.
Still learning.
Edit: On slower rigs, timerVal has to be set at multiples of its current setting for the same effect, thus it must vary from system to system.