pixelsearch Posted February 19 Posted February 19 (edited) Hi everybody This script displays a big MessageBox having a height higher than @DesktopHeight While the MessageBox is displayed, you can "navigate" inside it like this : * Mouse Left-click drag : to move the window up and down (displaying a new panel of lines) * Mouse Right-click : to display the window at its initial position * Up key * Down key * PageUp Key (Fn + up key on some laptops) * PageDown Key (Fn + down key on some laptops) * Home Key (Fn + left key on some laptops) * End Key (Fn + right key on some laptops) The 6 keyboard keys allow to navigate inside the window. Click on a button (placed at top of the window) to make your choice. All this could have been scripted more easily using a GUI, an Edit control containing the text to display, plus the buttons to choose from, but I just wanted to try it using a native MessageBox expandcollapse popup#include <APISysConstants.au3> ; $GCL_HCURSOR #include <ButtonConstants.au3> #include <EditConstants.au3> #include <GUIConstantsEx.au3> #include <Misc.au3> #include <MsgBoxConstants.au3> #include <StaticConstants.au3> #include <WinAPIRes.au3> ; _WinAPI_LoadCursor() #include <WinAPISysWin.au3> ; _WinAPI_SetClassLongEx() #include <WindowsConstants.au3> Opt("MustDeclareVars", 1) ;0=no, 1=require pre-declaration Opt("GUICloseOnESC", 0) ;1=ESC closes (default), 0=ESC won't close Global $g_hGUI, $g_hDLL = DllOpen("user32.dll") Global $g_aCaption, $g_sTitle Example() ;=========================================== Func Example() $g_hGUI = GUICreate("Big MsgBox example (7b)", 400, 200) GUICtrlCreateLabel("Number of lines in MsgBox (2 - 1588)", 10, 20, 180, 20, $SS_SUNKEN) Local $idNbLines = GUICtrlCreateInput("200", 200, 20, 35, 20, BitOR($GUI_SS_DEFAULT_INPUT, $ES_NUMBER)), $iNbLines GUICtrlSetLimit($idNbLines, 4) Local $idMsgBox = GUICtrlCreateButton("Big MsgBox", 10, 60, 100, 25, $BS_DEFPUSHBUTTON) Local $idExit = GUICtrlCreateButton("Exit", 10, 110, 100, 25) GUISetState() While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE, $idExit ExitLoop Case $idMsgBox Local $iRet = Prepare_MsgBox(GUICtrlRead($idNbLines)) If $iRet Then MsgBox($MB_TOPMOST, "Big MsgBox return value : " & $iRet, Retrieve_Caption($iRet), 0, $g_hGUI) GUICtrlSetState($idNbLines, $GUI_FOCUS) EndSwitch WEnd DllClose($g_hDLL) EndFunc ;==>Example ;=========================================== Func Prepare_MsgBox(Const $iNbLines) If $iNbLines < 2 Or $iNbLines > 1588 Then MsgBox($MB_TOPMOST, "Error", "Enter a number of lines between 2 and 1588", 0, $g_hGUI) Return ; 0 EndIf Local $sTxt = "Line 1 : first line" & @crlf For $i = 2 To $iNbLines - 1 $sTxt &= "Line " & $i & @crlf Next $sTxt &= "Line " & $iNbLines & " : last line" Dim $g_aCaption[3][3] = [ ["Cancel"], ["Try Again"], ["Continue"] ] ; 1st column = button caption, 2nd = button handle, 3rd = button ID $g_sTitle = "MsgBox with 3 buttons" Local $iChoice = _MsgBox(BitOr($MB_CANCELTRYCONTINUE, $MB_TOPMOST), $g_sTitle, $sTxt, 0, $g_hGUI) ;~ Dim $g_aCaption[2][3] = [ ["Yes"], ["No"] ] ;~ $g_sTitle = "MsgBox with 2 buttons" ;~ Local $iChoice = _MsgBox(BitOr($MB_YESNO, $MB_TOPMOST), $g_sTitle, $sTxt, 0, $g_hGUI) ;~ Dim $g_aCaption[1][3] = [ ["Hello"] ] ;~ $g_sTitle = "MsgBox with 1 button" ;~ Local $iChoice = _MsgBox(BitOr($MB_OK, $MB_TOPMOST), $g_sTitle, $sTxt, 0, $g_hGUI) Return $iChoice EndFunc ;==>Prepare_MsgBox ;=========================================== Func _MsgBox($iFlag, $g_sTitle, $sText, $iTimeOut = 0, $hWnd = 0) GUIRegisterMsg($WM_HELP , "WM_HELP") Local $hTimerProc = DllCallbackRegister('_MsgBoxTimerProc', 'none', 'hwnd;uint;uint_ptr;dword') Local $iTimerID = _WinAPI_SetTimer(0, 0, 10, DllCallbackGetPtr($hTimerProc)) Local $iChoice = MsgBox($iFlag, $g_sTitle, $sText, $iTimeOut, $hWnd) _WinAPI_KillTimer(0, $iTimerID) DllCallbackFree($hTimerProc) GUIRegisterMsg($WM_HELP, "") Return $iChoice EndFunc ;==>_MsgBox ;=========================================== Func _MsgBoxTimerProc($hWnd, $iMsg, $iTimerID, $iTime) If WinExists($g_sTitle) Then _WinAPI_KillTimer(0, $iTimerID) Local $hMsgBox = WinGetHandle($g_sTitle) For $i = 0 To Ubound($g_aCaption) - 1 ControlSetText($hMsgBox, "", "Button" & ($i + 1), $g_aCaption[$i][0]) $g_aCaption[$i][1] = ControlGetHandle($hMsgBox, "", "Button" & ($i + 1)) $g_aCaption[$i][2] = _WinAPI_GetDlgCtrlID($g_aCaption[$i][1]) ; remember OK button ID is 1 ($MB_OKCANCEL) or 2 ($MB_OK) ... ; ... msdn "If a message box has a Cancel button, the function returns the IDCANCEL value (2) if either the ESC key is pressed ; or the Cancel button is selected." ; "If the message box has no Cancel button, pressing ESC will no effect - unless an MB_OK button is present. ; If an MB_OK button is displayed and the user presses ESC, the return value will be IDOK (1)" [personal: only one case for this] Next Local $aPosMsgBox = WinGetPos($hMsgBox) Local $bTooHigh = ($aPosMsgBox[3] > @DesktopHeight + 15) ? True : False If $bTooHigh Then Local $aPosButton, $aPosStatic = ControlGetPos($hMsgBox, "", "Static1") ; the text area For $i = 0 To Ubound($g_aCaption) - 1 $aPosButton = ControlGetPos($hMsgBox, "", "Button" & ($i + 1)) WinMove($g_aCaption[$i][1], "", $aPosButton[0], $aPosStatic[1]) Next WinMove(ControlGetHandle($hMsgBox, "", "Static1"), "", $aPosStatic[0], $aPosStatic[1] + $aPosButton[3] + 10) _WinAPI_RedrawWindow($hMsgBox) ; +++ Send("{F1}") ; => Func WM_HELP EndIf EndIf EndFunc ;==>_MsgBoxTimerProc ;============================================== Func WM_HELP($hWnd, $iMsg, $wParam, $lParam) #forceref $hWnd, $iMsg, $wParam, $lParam If WinExists($g_sTitle) Then ; MessageBox window always exists at this stage Local $hMsgBox = WinGetHandle($g_sTitle) Local $aPosMsgBox = WinGetPos($hMsgBox), $aPosMsgBox_Init = $aPosMsgBox Local $aWinPos, $aMPos, $aMPosOld Local $hCursor = _WinAPI_LoadCursor(0, $OCR_SIZEALL) ; $OCR_SIZENS ok too Local $iPrev = _WinAPI_SetClassLongEx($hMsgBox, $GCL_HCURSOR, $hCursor) ; see "147c.au3" _ClearBuffer("0D") ; Enter key (in case pressed too long on button 'Big MsgBox' in main GUI, so it won't select a button in MsgBox) While 1 If WinActive($hMsgBox) Then Select Case _IsPressed("01", $g_hDLL) ; "01" = Left mouse button $aMPosOld = MouseGetPos() While _IsPressed("01", $g_hDLL) $aMPos = MouseGetPos() If $aMPos[1] <> $aMPosOld[1] Then $aWinpos = WinGetPos($hMsgBox) If ($aMPos[0] - 1 > $aWinpos[0]) And ($aMPos[0] + 1 < $aWinpos[0] + $aWinpos[2]) Then WinMove($hMsgBox, "", Default, $aWinpos[1] + ($aMPos[1] - $aMPosOld[1])) $aMPosOld = $aMPos EndIf EndIf Sleep(10) Wend Case _IsPressed("02", $g_hDLL) ; Right mouse button WinMove($hMsgBox, "", $aPosMsgBox_Init[0], $aPosMsgBox_Init[1]) Case _IsPressed("09", $g_hDLL) ; Tab key _ClearBuffer("09") _ChangeFocus($hMsgBox, "09") Case _IsPressed("0D", $g_hDLL) ; Enter key _ClearBuffer("0D") ControlClick($hMsgBox, "", ControlGetFocus($hMsgBox)) Case _IsPressed("1B", $g_hDLL) ; Esc key _ClearBuffer("1B") For $i = 0 To Ubound($g_aCaption) - 1 If $g_aCaption[$i][2] = 2 Then ; Cancel button (or OK button when alone : see msdn notes above) ControlClick($hMsgBox, "", $g_aCaption[$i][2]) EndIf Next Case _IsPressed("21", $g_hDLL) ; PageUp Key (Fn + up key on most laptops) _ClearBuffer("21") $aPosMsgBox = WinGetPos($hMsgBox) WinMove( $hMsgBox, "", Default, ($aPosMsgBox[1] + @DesktopHeight > 0) ? 0 : $aPosMsgBox[1] + @DesktopHeight ) Case _IsPressed("22", $g_hDLL) ; PageDown Key (Fn + down key on most laptops) _ClearBuffer("22") $aPosMsgBox = WinGetPos($hMsgBox) WinMove( $hMsgBox, "", Default, _ ($aPosMsgBox[3] + $aPosMsgBox[1] - @DesktopHeight < @DesktopHeight + 15) ? @DesktopHeight - $aPosMsgBox[3] : $aPosMsgBox[1] - @DesktopHeight ) Case _IsPressed("23", $g_hDLL) ; End Key (Fn + right key on most laptops) $aPosMsgBox = WinGetPos($hMsgBox) WinMove($hMsgBox, "", Default, @DesktopHeight - $aPosMsgBox[3]) Case _IsPressed("24", $g_hDLL) ; Home Key (Fn + left key on most laptops) WinMove($hMsgBox, "", Default, 0) Case _IsPressed("25", $g_hDLL) ; Left key _ClearBuffer("25") _ChangeFocus($hMsgBox, "25") Case _IsPressed("26", $g_hDLL) ; Up Key $aPosMsgBox = WinGetPos($hMsgBox) WinMove($hMsgBox, "", Default, ($aPosMsgBox[1] + 12 > 0) ? 0 : $aPosMsgBox[1] + 12) Case _IsPressed("27", $g_hDLL) ; Right key _ClearBuffer("27") _ChangeFocus($hMsgBox, "27") Case _IsPressed("28", $g_hDLL) ; Down Key $aPosMsgBox = WinGetPos($hMsgBox) WinMove( $hMsgBox, "", Default, _ ($aPosMsgBox[3] + $aPosMsgBox[1] - 12 < @DesktopHeight) ? @DesktopHeight - $aPosMsgBox[3] : $aPosMsgBox[1] - 12 ) EndSelect EndIf If Not BitAND(WinGetState($hMsgBox), $WIN_STATE_VISIBLE) Then ; one of MsgBox buttons was chosen by user ExitLoop EndIf Sleep(10) WEnd _WinAPI_SetClassLongEx($hMsgBox, $GCL_HCURSOR, $iPrev) ; needed, or cursor $OCR_SIZEALL will show... in final MsgBox from main loop EndIf Return $GUI_RUNDEFMSG EndFunc ;==>WM_HELP ;============================================== Func _ClearBuffer($sKey) While _IsPressed($sKey, $g_hDLL) Sleep(10) Wend EndFunc ;==>_ClearBuffer ;============================================== Func _ChangeFocus($hMsgBox, $sKey) Local $iCtrlGetFocus, $iCtrlSetFocus, $iStyle $iCtrlGetFocus = StringRight(ControlGetFocus($hMsgBox), 1) ; 1/2/3 (last char of "Button1"/"Button2"/"Button3") If $sKey = "27" Or $sKey = "09" Then ; Right key (27) or Tab (09) $iCtrlSetFocus = $iCtrlGetFocus < Ubound($g_aCaption) ? $iCtrlGetFocus + 1 : 1 Else ; Left key (25) $iCtrlSetFocus = $iCtrlGetFocus > 1 ? $iCtrlGetFocus - 1 : Ubound($g_aCaption) EndIf ControlFocus($hMsgBox, "", "Button" & $iCtrlSetFocus) $iStyle = _WinAPI_GetWindowLong($g_aCaption[$iCtrlGetFocus - 1][1], $GWL_STYLE) _WinAPI_SetWindowLong($g_aCaption[$iCtrlGetFocus - 1][1], $GWL_STYLE, BitXOR($iStyle, $BS_DEFPUSHBUTTON)) $iStyle = _WinAPI_GetWindowLong($g_aCaption[$iCtrlSetFocus - 1][1], $GWL_STYLE) _WinAPI_SetWindowLong($g_aCaption[$iCtrlSetFocus - 1][1], $GWL_STYLE, BitOR($iStyle, $BS_DEFPUSHBUTTON)) EndFunc ;==>_ChangeFocus ;=========================================== Func Retrieve_Caption($iRet) If Ubound($g_aCaption) = 1 Then ; $MB_OK Return $g_aCaption[0][0] Else For $i = 0 To Ubound($g_aCaption) - 1 If $g_aCaption[$i][2] = $iRet Then Return $g_aCaption[$i][0] ; button ID always = $iRet (except for $MB_OK, see note msdn above) Next EndIf MsgBox($MB_TOPMOST, "Warning", "This message should never be displayed", 0, $g_hGUI) EndFunc ;==>Retrieve_Caption Update Feb 24, 2025 : Left key, Right key, Tab, Enter, Spacebar, Esc These 6 keys react now like they do in any MsgBox Feb 27, 2025 : minor correction Edited February 27 by pixelsearch Feb 27, 2025 : script updated funkey, Musashi, argumentum and 1 other 4 "I think you are searching a bug where there is no bug..."
funkey Posted February 21 Posted February 21 Thank you. Works great. Only thing is visual issue at last line for me. But no problem. BR funkey pixelsearch 1 Programming today is a race between software engineers striving tobuild bigger and better idiot-proof programs, and the Universetrying to produce bigger and better idiots.So far, the Universe is winning.
pixelsearch Posted February 22 Author Posted February 22 (edited) Thanks to you guys, who found an interest in this experimental script @funkey tomorrow I'll be able to run the script on a Win11 laptop, to see if the little visual issue you described will show there. Meanwhile, I'm trying to add a few useful _IsPressed() keys inside Func WM_HELP() For example, in a MsgBox, one press on Right key (or on Tab) should focus the button on the right of the actual focused button. In the pic of my preceding post, the "Cancel" button should then be "unfocused" and the "Try again" button should be focused : Unfortunately, in the doctored MessageBox from the script, nothing happens when you press the Right key ! The beginning of the solution could be scripted like this : Case _IsPressed("27", $g_hDLL) ; Right key (nearly same code for Tab key = 09) While _IsPressed("27", $g_hDLL) Sleep(10) Wend Local $iCtrlGetFocus = StringRight(ControlGetFocus($hMsgBox), 1) ; 1/2/3 (last char of "Button1"/"Button2"/"Button3") Local $iCtrlSetFocus = $iCtrlGetFocus < Ubound($g_aCaption) ? $iCtrlGetFocus + 1 : 1 ControlFocus($hMsgBox, "", "Button" & $iCtrlSetFocus) ; correct display after window change. How to improve it ? 1) But that's not enough. If you insert the 7 preceding lines in the script (inside the While 1 loop of Func Help) then run the script, press one time the Right key, then... the focus doesn't change : it is still on the 'Cancel' button BUT now if you select manually another window, then back to the MsgBox window, you'll see that the focus IS correct ('Try again') . So the question is : what should be done easily to "refresh" the MsgBox so it displays correctly the new focused button ? Because swapping manually windows to refresh the focus isn't user friendly at all, _WinAPI_RedrawWindow or _WinAPI_SetWindowPos don't seem to help here (tested) . So if someone got a simple solution for this "focused button refresh", I'll take it, thanks. 2) In fact I just found a working solution for this "non refreshment" issue, based on $BS_DEFPUSHBUTTON : if we remove it from the button that doesn't have the focus anymore ('Cancel') and add it to the button having the focus ('Try again') then the display of the new focused button is always correct. So if no new idea appears during the week-end, then I'll add the $BS_DEFPUSHBUTTON part in the script, at the beginning of the week (taking care of Left key too etc...) Thanks for reading and have a great week-end everybody Edit Feb 24, 2025 : fixed all this in an updated script (1st post) Edited February 24 by pixelsearch "I think you are searching a bug where there is no bug..."
pixelsearch Posted February 24 Author Posted February 24 Update Feb 24, 2025 : Left key, Right key, Tab, Enter, Spacebar, Esc These 6 keys react now like they do in any MsgBox Listing updated in 1st post. "I think you are searching a bug where there is no bug..."
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now