Jump to content

Recommended Posts

Posted (edited)

Per-Monitor v2 DPI Awareness Scaling Test:

Have this GUI running and test by changing your DPI scaling settings from: Settings app > System > Display > Scale

Work in progress, but might become more interesting. Possible UDF if everything goes well.

#include <GuiEdit.au3>
#include <FontConstants.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPISysWin.au3>
#include <WinAPIGdi.au3>
#include <GUIConstantsEx.au3>
#include <AutoItConstants.au3>
#include <StructureConstants.au3>

; Apply PER_MONITOR_AWARE_V2 DPI Awareness
DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "int_ptr", -4)

Global $g_iScale = 1
Global $g_hCtrlFont = 0

Global Const $fDefFontSize = 8.5, $fDefPixel = $fDefFontSize * 96 / 72 

Global Const $WM_DPICHANGED = 0x02E0
Global Const $WM_GETDPISCALEDSIZE = 0x02E4
Global Const $WM_DPICHANGED_BEFOREPARENT = 0x02E2
Global Const $WM_DPICHANGED_AFTERPARENT = 0x02E3

OnAutoItExitRegister("_Cleanup")

Example()

Func Example()
    Local $iCtrlSpaceV = 15
    Local $sAppName = "Testing PerMonitorV2 DPI"
    Local $hGUI = GUICreate($sAppName, 440, 340, -1, -1)

    Local $idTestName = GUICtrlCreateLabel("Test Name:", 15, 20, -1, -1)
    Local $aPos = ControlGetPos($hGUI, "", $idTestName)
    Local $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV

    Local $idTestInput = GUICtrlCreateInput("Test Input", 15, $iPrev, 300, 24)
    Local $aPos = ControlGetPos($hGUI, "", $idTestInput)
    Local $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV

    Local $idCheckbox1 = GUICtrlCreateCheckbox("Test Checkbox1 ", 15, $iPrev)
    Local $aPos = ControlGetPos($hGUI, "", $idCheckbox1)
    Local $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV

    Local $idCheckbox2 = GUICtrlCreateCheckbox("Test Checkbox2 ", 15, $iPrev)
    Local $aPos = ControlGetPos($hGUI, "", $idCheckbox2)
    Local $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV

    _GUICtrlEdit_Create($hGUI, "This is a test" & @CRLF & "Another Line", 15, $iPrev, 394, 80)

    $g_iScale = _WinAPI_GetDPIForWindow($hGUI) / 96
    If @error Then $g_iScale = 1

    GUISetState(@SW_SHOW, $hGUI)

    GUIRegisterMsg($WM_DPICHANGED, _WM_DPICHANGED)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

        EndSwitch
    WEnd

    GUIDelete($hGUI)
EndFunc   ;==>Example

Func _WM_DPICHANGED($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam

    Local Static $iInitDPI = $g_iScale
    Local Static $iPrevDPI = $g_iScale

    ; Obtain new DPI values
    Local $iDPI = _WinAPI_LoWord($wParam)
    Local $iNewDPI = _WinAPI_LoWord($wParam) / 96

    Local $hFont = $g_hCtrlFont

    ; Create new scaled font
    $g_hCtrlFont = _WinAPI_CreateFont(-Round($fDefPixel * $iNewDPI), 0, 0, 0, $FW_NORMAL, False, False, False, _
            $DEFAULT_CHARSET, $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $PROOF_QUALITY, $DEFAULT_PITCH, "Segoe UI")

    ; Lock window drawing for smoother transition
    _WinAPI_LockWindowUpdate($hWnd)

    ; Enumerate child windows for sizing adjustments
    Local $aCtrls = _WinAPI_EnumChildWindows($hWnd, False)
    Local $hCtrl, $sClass, $aPos, $tPoint, $iX, $iY, $iW, $iH, $iOrigDPI
    If IsArray($aCtrls) Then
        For $i = 1 To $aCtrls[0][0]
            $hCtrl = $aCtrls[$i][0]
            $sClass = $aCtrls[$i][1]
            $aPos = WinGetPos($hCtrl)
            $tPoint = DllStructCreate("int X;int Y")
            DllStructSetData($tPoint, "X", $aPos[0])
            DllStructSetData($tPoint, "Y", $aPos[1])

            ; Convert screen coordinates to client
            _WinAPI_ScreenToClient($hWnd, $tPoint)
            $iX = DllStructGetData($tPoint, "X")
            $iY = DllStructGetData($tPoint, "Y")
            $iW = _WinAPI_GetWindowWidth($hCtrl)
            $iH = _WinAPI_GetWindowHeight($hCtrl)

            ; Divide value by previous DPI factor to get original size, then multiply by new DPI factor (rounded to nearest .5)
            $iX = Round((($iX / $iPrevDPI) * $iNewDPI) / 0.5) * 0.5
            $iY = Round((($iY / $iPrevDPI) * $iNewDPI) / 0.5) * 0.5
            $iW = Round((($iW / $iPrevDPI) * $iNewDPI) / 0.5) * 0.5
            $iH = Round((($iH / $iPrevDPI) * $iNewDPI) / 0.5) * 0.5

            ; Set new position/sizes for control
            Local $iCtrlID = _WinAPI_GetDlgCtrlID($hCtrl)
            If $iCtrlID < 10000 Then
                GUICtrlSetResizing($iCtrlID, $GUI_DOCKLEFT + $GUI_DOCKTOP + $GUI_DOCKWIDTH + $GUI_DOCKHEIGHT)
                GUICtrlSetPos($iCtrlID, $iX, $iY, $iW, $iH) ; working for GUICtrlCreate* controls only
            ElseIf $iCtrlID >= 10000 Then
                ControlMove($hWnd, "", $iCtrlID, $iX, $iY, $iW, $iH) ; working for UDF-created controls only
            ElseIf Not $iCtrlID Then
                ; probably custom SysLink or something which has no CtrlID
                ; can't move without CtrlID
                ; only need to modify X here
            EndIf

            ; Set new scaled font to each control
            _SendMessage($hCtrl, $WM_SETFONT, $g_hCtrlFont, True)
        Next
    EndIf

    ; Resize GUI window
    Local $tRECT = DllStructCreate($tagRECT, $lParam)
    Local $iX = $tRECT.left, $iY = $tRECT.top, $iW = $tRECT.right - $iX, $iH = $tRECT.bottom - $iY
    _WinAPI_SetWindowPos($hWnd, 0, $iX, $iY, $iW, $iH, BitOR($SWP_NOZORDER, $SWP_NOACTIVATE))

    ;
    ; IMPORTANT: Desperately need a GUICtrlGetResizing() function to restore original control resizing behavior
    ;

    ; Update previous DPI with the now current DPI
    $iPrevDPI = $iNewDPI

    _WinAPI_LockWindowUpdate(0)

    ; Delete previous font
    If $hFont Then _WinAPI_DeleteObject($hFont)

    Return 0
EndFunc   ;==>_WM_DPICHANGED

Func _Cleanup()
    _WinAPI_DeleteObject($g_hCtrlFont)
EndFunc

Func _WinAPI_GetDPIForWindow($hWnd) ; UEZ
    Local $aResult = DllCall('user32.dll', "uint", "GetDpiForWindow", "hwnd", $hWnd) ;requires Win10 v1607+ / no server support
    If Not IsArray($aResult) Or @error Then Return SetError(1, @extended, 0)
    If Not $aResult[0] Then Return SetError(2, @extended, 0)
    Return $aResult[0]
EndFunc   ;==>_WinAPI_GetDPIForWindow

 

Edited by WildByDesign

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
×
×
  • Create New...