Jump to content

Recommended Posts

Posted (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

#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

Global $g_hGUI, $g_hDLL = DllOpen("user32.dll")
Global $g_aCaption, $g_sTitle

Example()

;===========================================
Func Example()

    $g_hGUI = GUICreate("Big MsgBox example", 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, 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] = ["Cancel", "Try Again", "Continue"]
    $g_sTitle = "MsgBox with 3 buttons"
    Local $iChoice = _MsgBox(BitOr($MB_CANCELTRYCONTINUE, $MB_TOPMOST), $g_sTitle, $sTxt, 0, $g_hGUI)

;~  Dim $g_aCaption[2] = ["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] = ["OK"]
;~  $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)
        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
                ControlSetText($hMsgBox, "", "Button" & ($i + 1), $g_aCaption[$i])
                $aPosButton = ControlGetPos($hMsgBox, "", "Button" & ($i + 1))
                WinMove(ControlGetHandle($hMsgBox, "", "Button" & ($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
        Else
            For $i = 0 To Ubound($g_aCaption) - 1
                ControlSetText($hMsgBox, "", "Button" & ($i + 1), $g_aCaption[$i])
            Next
        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"

        HotKeySet("{SPACE}", "_Space")

        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("21", $g_hDLL) ; PageUp Key
                        While _IsPressed("21", $g_hDLL)
                            Sleep(10)
                        Wend
                        $aPosMsgBox = WinGetPos($hMsgBox)
                        WinMove( $hMsgBox, "", Default, ($aPosMsgBox[1] + @DesktopHeight > 0) ? 0 : $aPosMsgBox[1] + @DesktopHeight )

                    Case _IsPressed("22", $g_hDLL) ; PageDown Key
                        While _IsPressed("22", $g_hDLL)
                            Sleep(10)
                        Wend
                        $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
                        $aPosMsgBox = WinGetPos($hMsgBox)
                        WinMove($hMsgBox, "", Default, @DesktopHeight - $aPosMsgBox[3])

                    Case _IsPressed("24", $g_hDLL) ; Home Key
                        WinMove($hMsgBox, "", Default, 0)

                    Case _IsPressed("26", $g_hDLL) ; Up Key
                        $aPosMsgBox = WinGetPos($hMsgBox)
                        WinMove($hMsgBox, "", Default, ($aPosMsgBox[1] + 12 > 0) ? 0 : $aPosMsgBox[1] + 12)

                    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 clicked
                ExitLoop
            EndIf

            Sleep(10)
        WEnd

        HotKeySet("{SPACE}")

        _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 _Space()

    HotKeySet("{SPACE}") ; deactivate Hotkey

    If WinActive($g_sTitle) Then
        HotKeySet("{SPACE}", "_DoNothing") ; this part to avoid a MsgBox button being triggered, when Spacebar key is pressed too long.
        While _IsPressed("20", $g_hDLL) ; Spacebar key
            Sleep(10)
        Wend
    Else
        Send("{SPACE}")
    EndIf

    HotKeySet("{SPACE}", "_Space") ; reactivate Hotkey
EndFunc   ;==>_Space

;==============================================
Func _DoNothing()

EndFunc   ;==>_DoNothing

BigMsgBox.png.efda2805087f4739eaa38b3ed5fc6325.png

Edited by pixelsearch
typo

"I think you are searching a bug where there is no bug..."

Posted

Thank you. Works great.

Only thing is visual issue at last line for me. But no problem.

image.png.db743817ef613a1f29f5a7aee1ba1731.png

BR

funkey

Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs, and the Universe
trying to produce bigger and better idiots.
So far, the Universe is winning.

Posted

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 (does it appear on your side each time you run the script ?)

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 :

1rijghtkeypressed.png.7fd2a4dc4d04ab5b87619a41fbfa2a5b.png

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 :bye:

"I think you are searching a bug where there is no bug..."

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   1 member

×
×
  • Create New...