#include <WindowsConstants.au3>
#include <GUIConstants.au3>
#include <GDIPlus.au3>
#include <WinAPI.au3>

Global Const $DPI_AWARENESS_CONTEXT_UNAWARE = -1
Global Const $DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2
Global Const $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = -3
Global Const $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4
Global Const $DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = -5

Global Const $WM_DPICHANGED = 0x02E0

_WinAPI_SetProcessDpiAwarenessContext($DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)

; Achtung - Es wird die DPI von Monitor 1 genutzt.
; Hier sollte manuell angepasst werden damit die DPI von dem Monitor übernommen wird auf dem das Fenster spawnt.
;
; In der finalen Version fällt das natürlich weg.

Global $iCurrentDPI = _WinAPI_GetDpiForMonitor(1)
Global $fDPIScalefactor = $iCurrentDPI / 96

Global Const $GUI_CLIENT_WIDTH = 314
Global Const $GUI_CLIENT_HEIGHT = 130

Global $hGUI = GUICreate("Beispiel 1", $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT, -1, -1)

Global $hLabel1 = GUICtrlCreateLabel("Label1", 16, 16, 36, 21)
GUICtrlSetBkColor(-1, 0x3399FF)

Global $hLabel2 = GUICtrlCreateLabel("Label2", 64, 16, 36, 21)
GUICtrlSetBkColor(-1, 0x3399FF)

Global $hLabel3 = GUICtrlCreateLabel("Label3", 112, 16, 36, 21)
GUICtrlSetBkColor(-1, 0x3399FF)

Global $Input1 = GUICtrlCreateInput("Input1", 160, 16, 137, 21)

Global $hButton = GUICtrlCreateButton("Schließen", 16, 48, 283, 65)

Local $aGUI_Controls[2] = [ $hLabel1, $hButton ]

GUISetState(@SW_HIDE, $hGUI)

_GUI_SetResizing($hGUI, $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT, $aGUI_Controls)
GUIRegisterMsg($WM_DPICHANGED, _WM_DPICHANGED)

GUISetState(@SW_SHOW, $hGUI)

While Sleep(10)
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE, $hButton
			GUIDelete($hGUI)
			ExitLoop

	EndSwitch
WEnd

Func _WM_DPICHANGED($hWnd, $iMsg, $wParam, $lParam)
	GUIRegisterMsg($WM_DPICHANGED, "")

	Local $iNewDPI = _WinAPI_HiWord($wParam)
	Local $fNewDPIScalefactor = $iNewDPI / 96

	;Windows sendet die Nachricht teilweise mehrfach und wir brauchen nicht
	;das Fenster unnötig zu skalieren.
	If $iCurrentDPI = $iNewDPI Then
		GUIRegisterMsg($WM_DPICHANGED, _WM_DPICHANGED)
		Return $GUI_RUNDEFMSG
	EndIf

	ConsoleWrite("New DPI: " & $iNewDPI & ", ScaleFactor: " & $fNewDPIScalefactor & @CRLF)

	For $i = $hLabel1 To $hButton
		;Hole die hFont des aktuellen Controls
		Local $hFont = GUICtrlSendMsg($i, $WM_GETFONT, 0, 0)

		;Erzeuge ein Struct mit LogFontA wo hFont reingespeichert wird
		Local $tLogFontA = DllStructCreate("long lfHeight;long lfWidth;long lfEscapement;long lfOrientation;" & _
										   "long lfWeight;byte lfItalic;byte lfUnderline;byte lfStrikeOut;" & _
										   "byte lfCharSet;byte lfOutPrecision;byte lfClipPrecision;byte lfQuality;" & _
										   "byte lfPitchAndFamily;char lfFaceName[1024];")

		;Speichere hFont in LogFontA
		_WinAPI_GetObject($hFont, DllStructGetSize($tLogFontA), DllStructGetPtr($tLogFontA))

		;Zeige aktuelle Größe an
		ConsoleWrite("-> " & GUICtrlRead($i) & " - Height: " & DllStructGetData($tLogFontA, "lfHeight") & " - Width: " & DllStructGetData($tLogFontA, "lfWidth") & @CRLF)

		;Berechne neue Größe
		Local $fNormalizedHeight = DllStructGetData($tLogFontA, "lfHeight") / $fDPIScalefactor
		Local $fNewHeight = -Round(Abs($fNormalizedHeight * $fNewDPIScalefactor), 0)

		;Setze neue Größe
		;Wir verwenden CreateFontIndirect und nicht CreateFontA, damit wir nicht alle Attribute kopieren müssen
		;hier müssen wir nur ein Parameter verändern und können das Struct übergeben.
		DllStructSetData($tLogFontA, "lfHeight", $fNewHeight)
		Local $hFont = _WinAPI_CreateFontIndirect($tLogFontA)

		;Zeige neue Größe an
		ConsoleWrite("!>" & GUICtrlRead($i) & " - Height: " & $fNewHeight & @CRLF)

		;Setze neue Font
		GUICtrlSendMsg($i, $WM_SETFONT, $hFont, True)
;~ 		_WinAPI_SetFont(GUICtrlGetHandle($i), $hFont, True)
	Next

	$iCurrentDPI = $iNewDPI
	$fDPIScalefactor = $fNewDPIScalefactor

	_GUI_Move($hGUI, Default, Default, $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT)

	;Registriere DPI-changed wieder
	GUIRegisterMsg($WM_DPICHANGED, _WM_DPICHANGED)

	Return $GUI_RUNDEFMSG
EndFunc

Func _WinAPI_SetProcessDpiAwarenessContext($DPIAwareContext, $hGUI = 0) ;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setprocessdpiawarenesscontext
    Local $hDC = _WinAPI_GetDC($hGUI)
    Local $aResult1 = DllCall("user32.dll", "ptr", "GetDpiFromDpiAwarenessContext", "ptr", $hDC)
    If Not $hGUI Then $hGUI = WinGetHandle(AutoItWinGetTitle())
    Local $aResult2 = DllCall("user32.dll", "ptr", "GetWindowDpiAwarenessContext", "ptr", $hGUI)
    Local $aResult3 = DllCall("user32.dll", "ptr", "GetThreadDpiAwarenessContext")
    Local $aResult4 = DllCall("user32.dll", "int", "GetAwarenessFromDpiAwarenessContext", "ptr", $aResult2[1])
    _WinAPI_ReleaseDC(0, $hDC)
    $DPIAwareContext = ($DPIAwareContext < -5) ? -5 : ($DPIAwareContext > -1) ? -1 : $DPIAwareContext
    Local $aResult = DllCall("user32.dll", "Bool", "SetProcessDpiAwarenessContext", "int", $aResult1[0] + $DPIAwareContext)
    If @error Or Not $aResult[0] Then Return SetError(1, 0, 0)
    Return 1
EndFunc

Func _WinAPI_GetDpiForMonitor($iMonitor)
	Local Const $MDT_DEFAULT = 0

    Local $aMonitors = _WinAPI_EnumDisplayMonitors()
    Local $x, $y
    Local $aRet = DllCall("Shcore.dll", "long", "GetDpiForMonitor", "long", $aMonitors[$iMonitor][0], "int", $MDT_DEFAULT, "uint*", $x, "uint*", $y)
    If @error Or Not IsArray($aRet) Then Return SetError(1, 0, 0)
    Return $aRet[3]
EndFunc

Func _GUI_GetBorderSize($hGUI)
	Local $aClientSize = WinGetClientSize($hGUI)
	Local $aSize = WinGetPos($hGUI)

	Local $aReturn[2] = [ $aSize[2] - $aClientSize[0], $aSize[3] - $aClientSize[1] ]
	Return $aReturn
EndFunc

Func _GUI_Move($hGUI, $iX, $iY, $iWidth, $iHeight, $bIgnoreDPI = False)
	Local $aBorders = _GUI_GetBorderSize($hGUI)

	If $iX = -1 Then $iX = (@DesktopWidth - $iWidth * ($bIgnoreDPI ? 1 : $fDPIScalefactor) - $aBorders[0]) / 2
	If $iY = -1 Then $iY = (@DesktopHeight - $iHeight * ($bIgnoreDPI ? 1 : $fDPIScalefactor) - $aBorders[1]) / 2

	If Not $bIgnoreDPI Then
		$iWidth = $iWidth * $fDPIScalefactor
		$iHeight = $iHeight * $fDPIScalefactor
	EndIf

	$iWidth += $aBorders[0]
	$iHeight += $aBorders[1]

	Return WinMove( _
		$hGUI, _
		"", _
		$iX, _
		$iY, _
		$iWidth, _
		$iHeight _
	)
EndFunc

Func _GUI_SetResizing($hGUI, $iWidth, $iHeight, $aControls)
	_GUI_Move($hGUI, Default, Default, $iWidth, $iHeight, True)

	If Not UBound($aControls, 2) Then
		For $i = $aControls[0] To $aControls[1]
			If _WinAPI_GetClassName(GUICtrlGetHandle($i + 1)) = "msctls_updown32" Then ContinueLoop
			GUICtrlSetResizing($i, $GUI_DOCKAUTO)
		Next

		_GUI_Move($hGUI, Default, Default, $iWidth, $iHeight)
	Else
		For $i = 0 To UBound($aControls) - 1
			If _WinAPI_GetClassName(GUICtrlGetHandle($aControls[$i][0] + 1)) = "msctls_updown32" Then ContinueLoop
			GUICtrlSetResizing($aControls[$i][0], $GUI_DOCKAUTO)
		Next

		_GUI_Move($hGUI, Default, Default, $iWidth, $iHeight)

		For $i = 0 To UBound($aControls) - 1
			If _WinAPI_GetClassName(GUICtrlGetHandle($aControls[$i][0] + 1)) = "msctls_updown32" Then ContinueLoop
			GUICtrlSetResizing($aControls[$i][0], $aControls[$i][1])
		Next
	EndIf
EndFunc