Jump to content

GUIGetMsg() and Dragging a TITLE BAR to the top


Recommended Posts

In Win10, you can drag the TITLE BAR to the top of the monitor and the GUI will maximize. GUIGetMsg() does not trigger the event.
I believe to understand why, but maybe there can be a fix for the unexpected behavior in GUIGetMsg() not triggering the event.

Here is a sampler to show expected behavior via WM_WINDOWPOSCHANGING > _WinAPI_GetWindowPlacement()

#include <AutoItConstants.au3>
#include <GUIConstants.au3>
#include <WinAPIMisc.au3>
#include <WinAPIGdi.au3>
#include <WinAPISysWin.au3> ; _WinAPI_GetWindowPlacement
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <SendMessage.au3>

Global Const $SC_DRAGMOVE = 0xF012

Global $__g_WINDOWPOSCHANGING_iGap = 10 ; 30 pixels for "dramatic effect", 10 pixels should be good for the "magnetic pull"
Global $__g_WINDOWPOSCHANGING_iEdge = BitOR(1, 2, 4, 8) ; = 15 ; Left, Top, Right, Bottom
Global $__g_WINDOWPOSCHANGING_aBorderWidth[4] = [0, 0, 0, 0] ; Left, Top, Right, Bottom ; for _SetGETMINMAXINFOBorderWidth()
Global $__g_aGETMINMAXINFO[4] = [0,0,0,0] ; for WM_GETMINMAXINFO() to limit GUI minimum and/or maximum size. Default of zero means disable.
Global $idBtnStyle, $iTest_GUIGetMsg_ON = 1 ; <<<< set to zero to show the expected behaviour

Example()
Func Example()
    Local $hGUI = GUICreate("Snapped Window multi-monitor", 500, 300)
    _SetGETMINMAXINFO(300, 200, 800)
    ConsoleWrite('@OSVersion = ' & @OSVersion & @CRLF)
    If @OSVersion <> "WIN_10" Then _SetGETMINMAXINFOBorderWidth("OFF")
    GUISetBkColor(0x505254)
    Local $bNewStyle = True ; False
    $idBtnStyle = GUICtrlCreateButton("Undo Style", 5, 50, 200, 25)
    GUIRegisterMsg($WM_WINDOWPOSCHANGING, "WM_WINDOWPOSCHANGING")
    GUIRegisterMsg($WM_GETMINMAXINFO, "WM_GETMINMAXINFO")
    GUISetStyle(BitOR($GUI_SS_DEFAULT_GUI, $WS_MAXIMIZEBOX, $WS_SIZEBOX, $WS_THICKFRAME, $WS_TABSTOP))
    _GUIStyleRefresh($hGUI)
    _SetGETMINMAXINFOBorderWidth($hGUI)
    _GuiCenterOnMonitorFromMousePosition($hGUI)
    GUISetState()

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                GUIDelete()
                ExitLoop

             Case $GUI_EVENT_PRIMARYDOWN
                _SendMessage($hGUI, $WM_SYSCOMMAND, $SC_DRAGMOVE, 0)

;               Moved to WM_WINDOWPOSCHANGING() for this example. GUIGetMsg() does not act on size events when the TITLE bar is dragged.
            Case $GUI_EVENT_MAXIMIZE, $GUI_EVENT_RESTORE ; should not change to this style while maximized
                If $iTest_GUIGetMsg_ON Then GUICtrlSetState($idBtnStyle, (BitAND(WinGetState($hGUI), $WIN_STATE_MAXIMIZED) ? $GUI_DISABLE : $GUI_ENABLE))

            Case $idBtnStyle
                $bNewStyle = Not $bNewStyle
                If $bNewStyle Then
                    GUISetStyle(BitOR($GUI_SS_DEFAULT_GUI, $WS_MAXIMIZEBOX, $WS_SIZEBOX, $WS_THICKFRAME, $WS_TABSTOP))
                Else
                    GUISetStyle($GUI_SS_DEFAULT_GUI)
;~                  GUISetStyle($WS_POPUP)
                EndIf
                GUICtrlSetData($idBtnStyle, ($bNewStyle ? 'Undo Style' : 'Change Style'))
                _GUIStyleRefresh($hGUI)
                _SetGETMINMAXINFOBorderWidth($hGUI)
                _GUIStyleRefresh($hGUI) ; ..with new SetGETMINMAXINFOBorderWidth


        EndSwitch
    WEnd
EndFunc   ;==>Example

Func _SetGETMINMAXINFO($iMinTrackSizeX = Default, $iMinTrackSizeY = Default, $iMaxTrackSizeX = Default, $iMaxTrackSizeY = Default)
;~  $tagMINMAXINFO = 'struct; long ReservedX;long ReservedY;long MaxSizeX;long MaxSizeY;long MaxPositionX;long MaxPositionY;long MinTrackSizeX;long MinTrackSizeY;long MaxTrackSizeX;long MaxTrackSizeY; endstruct'
    If $iMinTrackSizeX <> Default Then $__g_aGETMINMAXINFO[0] = $iMinTrackSizeX ; https://www.autoitscript.com/forum/topic/144385-restricting-the-minimum-and-maximum-sizes-of-a-gui/
    If $iMinTrackSizeY <> Default Then $__g_aGETMINMAXINFO[1] = $iMinTrackSizeY
    If $iMaxTrackSizeX <> Default Then $__g_aGETMINMAXINFO[2] = $iMaxTrackSizeX
    If $iMaxTrackSizeY <> Default Then $__g_aGETMINMAXINFO[3] = $iMaxTrackSizeY
    Return $__g_aGETMINMAXINFO
EndFunc

Func WM_GETMINMAXINFO($hWnd, $msg, $wParam, $lParam)
    Local $tagMINMAXINFO = DllStructCreate("struct;long;long;long;long;long;long;long MinTrackSizeX;long MinTrackSizeY;long MaxTrackSizeX;long MaxTrackSizeY;endstruct", $lParam)
    If $__g_aGETMINMAXINFO[0] Then $tagMINMAXINFO.MinTrackSizeX = $__g_aGETMINMAXINFO[0] ; DllStructSetData($tagMINMAXINFO, 7, $__g_aGETMINMAXINFO[0]) ; min X
    If $__g_aGETMINMAXINFO[1] Then $tagMINMAXINFO.MinTrackSizeY = $__g_aGETMINMAXINFO[1]
    If $__g_aGETMINMAXINFO[2] Then $tagMINMAXINFO.MaxTrackSizeX = $__g_aGETMINMAXINFO[2]
    If $__g_aGETMINMAXINFO[3] Then $tagMINMAXINFO.MaxTrackSizeY = $__g_aGETMINMAXINFO[3]
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_GETMINMAXINFO

Func _SetGETMINMAXINFOBorderWidth($hWnd, $iLeft = Default, $iTop = Default, $iRight = Default, $iBottom = Default )
    Local Static $iAutoBorderWidthEnabled = 1
    Switch $hWnd
        Case "ON"
            $iAutoBorderWidthEnabled = 1
            Return SetError(0, $iAutoBorderWidthEnabled, $__g_WINDOWPOSCHANGING_aBorderWidth)
        Case "OFF"
            $iAutoBorderWidthEnabled = 0
            Return SetError(0, $iAutoBorderWidthEnabled, $__g_WINDOWPOSCHANGING_aBorderWidth)
        Case "SET"
            If $iLeft <> Default Then $__g_WINDOWPOSCHANGING_aBorderWidth[0] = $iLeft
            If $iTop <> Default Then $__g_WINDOWPOSCHANGING_aBorderWidth[1] = $iTop
            If $iRight <> Default Then $__g_WINDOWPOSCHANGING_aBorderWidth[2] = $iRight
            If $iBottom <> Default Then $__g_WINDOWPOSCHANGING_aBorderWidth[3] = $iBottom
            Return SetError(0, $iAutoBorderWidthEnabled, $__g_WINDOWPOSCHANGING_aBorderWidth)
        Case "RESET"
            $__g_WINDOWPOSCHANGING_aBorderWidth[0] = 0
            $__g_WINDOWPOSCHANGING_aBorderWidth[1] = 0
            $__g_WINDOWPOSCHANGING_aBorderWidth[2] = 0
            $__g_WINDOWPOSCHANGING_aBorderWidth[3] = 0
            Return SetError(0, $iAutoBorderWidthEnabled, $__g_WINDOWPOSCHANGING_aBorderWidth)
    EndSwitch
    If Not $iAutoBorderWidthEnabled Then Return
    Local $aWinPos = WinGetPos($hWnd)
    Local $aWinClientSize = WinGetClientSize($hWnd)
    Local $iBorder = (($aWinPos[2] - $aWinClientSize[0]) / 2)
    Local $iHeader = $aWinPos[3] - $aWinClientSize[1] - $iBorder
    ConsoleWrite( '>>> ' & $iBorder & @TAB & $iHeader & @CRLF)
    If $iBorder Then $iBorder -= 1 ; auto-adjusting for Windows 10 border ; -1 or will otherwise show part in the adjacent monitor in multi-monitor setup.
    $__g_WINDOWPOSCHANGING_aBorderWidth[0] = - $iBorder
    $__g_WINDOWPOSCHANGING_aBorderWidth[2] = $iBorder
    $__g_WINDOWPOSCHANGING_aBorderWidth[3] = $iBorder
    Return SetError(0, $iAutoBorderWidthEnabled, $__g_WINDOWPOSCHANGING_aBorderWidth)
EndFunc

Func _GUIStyleRefresh($hWnd)
    Local $aWinPos = WinGetPos($hWnd)
    If @error Then Return SetError(@error, @extended, @error)
    WinMove($hWnd, "", $aWinPos[0], $aWinPos[1], $aWinPos[2] - 1, $aWinPos[3] - 1)
    WinMove($hWnd, "", $aWinPos[0], $aWinPos[1], $aWinPos[2], $aWinPos[3])
    Return
EndFunc   ;==>GUIStyleRefresh

Func WM_WINDOWPOSCHANGING($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam
    ; original code at https://www.autoitscript.com/forum/topic/24342-form-snap/
;~  ConsoleWrite('+ WM_WINDOWPOSCHANGING(' & $hWnd & ', ' & $iMsg & ', ' & $wParam & ', ' & $lParam & ') ' & @SEC &'.'& @MSEC & @CRLF)

    ; The Static declaration speeds up the func. ( not much, nanoseconds )
    Local Static $stWinPos, $tPos, $hMonitor, $aData, $nLeft, $nTop, $nRight, $nBottom, $iShowCmd = 0, $hTimer = 0

    If TimerDiff($hTimer) > 1000 Then
        $hTimer = TimerInit()
        $tPos = _WinAPI_GetMousePos()                                  ;  These func are very time consuming and
        If Not @error Then $hMonitor = _WinAPI_MonitorFromPoint($tPos) ;  the user is very unlikely to move the
        If Not @error Then $aData = _WinAPI_GetMonitorInfo($hMonitor)  ;  mouse into position on another monitor
        If @error Then Return $GUI_RUNDEFMSG                           ;  in less than a second.
    EndIf

    ; https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-windowpos?redirectedfrom=MSDN
    $stWinPos = DllStructCreate("hwnd hwnd;hwnd hwndInsertAfter;int x;int y;int cx;int cy;uint flags", $lParam) ; $tagWINDOWPOS
    $nLeft = $aData[1].Left
    $nTop = $aData[1].Top     ; $tagRECT = "struct;long Left;long Top;long Right;long Bottom;endstruct"
    $nRight = $aData[1].Right - $stWinPos.cx
    $nBottom = $aData[1].Bottom - $stWinPos.cy

    If BitAND($stWinPos.flags, 0x0020) Then ; SWP_FRAMECHANGED ; https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-windowpos
        Local $tRET = _WinAPI_GetWindowPlacement($hWnd)
        If @error = 0 Then ; https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-windowplacement
            $iShowCmd = $tRET.showCmd ; $tagWINDOWPLACEMENT = "uint length;uint flags;uint showCmd;long ptMinPosition[2];long ptMaxPosition[2];long rcNormalPosition[4]"

            Local Static $aShowCmd[10] = ["SW_HIDE","SW_SHOWNORMAL","SW_SHOWMINIMIZED","SW_MAXIMIZE","SW_SHOWNOACTIVATE","SW_SHOW","SW_MINIMIZE","SW_SHOWMINNOACTIVE","SW_SHOWNA","SW_RESTORE"]
            ConsoleWrite($aShowCmd[$iShowCmd] & @CRLF) ; ..just for show

            ; dragging the title will change state and GUIGetMsg() will not act on it, so is better running here.
            If $iTest_GUIGetMsg_ON = 0 Then GUICtrlSetState($idBtnStyle, $iShowCmd = @SW_MAXIMIZE ? $GUI_DISABLE : $GUI_ENABLE)
        EndIf
    EndIf

    If $iShowCmd = @SW_MAXIMIZE Then Return $GUI_RUNDEFMSG

    If BitAND($__g_WINDOWPOSCHANGING_iEdge, 1) And Abs($nLeft - $stWinPos.x) <= $__g_WINDOWPOSCHANGING_iGap Then $stWinPos.x = $nLeft + $__g_WINDOWPOSCHANGING_aBorderWidth[0]
    If BitAND($__g_WINDOWPOSCHANGING_iEdge, 4) And Abs($nRight - $stWinPos.x) <= $__g_WINDOWPOSCHANGING_iGap Then $stWinPos.x = $nRight + $__g_WINDOWPOSCHANGING_aBorderWidth[2]
    If BitAND($__g_WINDOWPOSCHANGING_iEdge, 8) And Abs($nBottom - $stWinPos.y) <= $__g_WINDOWPOSCHANGING_iGap Then $stWinPos.y = $nBottom + $__g_WINDOWPOSCHANGING_aBorderWidth[3]
    If BitAND($__g_WINDOWPOSCHANGING_iEdge, 2) And Abs($nTop - $stWinPos.y) <= $__g_WINDOWPOSCHANGING_iGap Then $stWinPos.y = $nTop + $__g_WINDOWPOSCHANGING_aBorderWidth[1]

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_WINDOWPOSCHANGING

Func _GuiCenterOnMonitorFromMousePosition($hForm, $sText = "")
    ; https://www.autoitscript.com/forum/index.php?showtopic=139260&view=findpost&p=1479329
    Local $aData, $hMonitor, $tPos = _WinAPI_GetMousePos()
    If Not @error Then $hMonitor = _WinAPI_MonitorFromPoint($tPos)
    If Not @error Then $aData = _WinAPI_GetMonitorInfo($hMonitor)
    If @error Then Return SetError(1, -1, 1)
    Local $iWinState = WinGetState($hForm, $sText)
    If @error Then Return SetError(2, $aData[2], 2)
    If BitAND($iWinState, $WIN_STATE_MINIMIZED) Or _
            BitAND($iWinState, $WIN_STATE_MAXIMIZED) Then WinSetState($hForm, $sText, @SW_RESTORE)
    Local $aWinPos = WinGetPos($hForm, $sText)
    If @error Then Return SetError(3, $aData[2], 3)
    WinMove($hForm, _
            $sText, _
            Int((($aData[0].Right - $aData[0].Left - $aWinPos[2]) / 2) + $aData[0].Left), _
            Int((($aData[0].Bottom - $aData[0].Top - $aWinPos[3]) / 2) + $aData[0].Top))
    If @error Then Return SetError(4, $aData[2], 4)
    If BitAND($iWinState, $WIN_STATE_MINIMIZED) Then WinSetState($hForm, $sText, @SW_MINIMIZE)
    If BitAND($iWinState, $WIN_STATE_MAXIMIZED) Then WinSetState($hForm, $sText, @SW_MAXIMIZE)
    Return SetError(0, $aData[2], 0)
EndFunc   ;==>GuiCenterOnMonitorFromMousePosition

...well, a sampler that is like a "example type of script" but I did not know where to post it to ask if it deserves opening a ticket or not. Tho it should.
@jpm, will you give your 2 cents :) 

Edit: $GUI_EVENT_RESIZED does trigger ( OnEvent or not ).
  Maybe the patch can be around there in the source ?, no clue.

Edited by argumentum
Link to post
Share on other sites

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   0 members

    No registered users viewing this page.

×
×
  • Create New...