#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>

#include <File.au3>
#include <ListViewConstants.au3>
#include <WinAPISys.au3>

Global Const $I_METRICS_APPLIEDDPI = RegRead("HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics", "AppliedDPI")
Global Const $F_DPI_SCALEFACTOR = Round($I_METRICS_APPLIEDDPI / 96, 2)

Example1() ; GUI wird wie ein Bild skaliert. Alle Controls wachsen.
Example2() ; GUI wird nach selbst gesetztem Resizing skaliert. Controls wachsen bestimmt. (Fenster ist sizebar)
Example3() ; Konkreter Anwendungsfall (Fenster ist sizebar)
Example4() ; GUI wird wie ein Bild skaliert. Mit "UDF" (Fenster ist sizebar)
Example5() ; ACHTUNG BUG - GUI wird wie ein Bild skaliert. Alle Controls wachsen. Mit "UDF"

#cs Beispiel 1

	Skalierbare GUI, so dass alle Controls mit dem Fensterseitenverhältnis mitskalieren.
	Etwa so als ob man ein Bild skaliert.

	Wie funktionierts?

		Erzeuge die GUI mittels festgelegter Werte: $GUI_WIDTH, $GUI_HEIGHT
		Das sind die Clientgrößen des Fensters (der Arbeitsbereich, dort wo man Controls platzieren kann)

		1. Setze das Resizing aller Controls auf $GUI_DOCKAUTO (einige Controls haben standardmäßig andere Resizings, deshalb $GUI_DOCKAUTO)
		2. Zeige die GUI versteckt an, damit die Controls generiert werden können und die Skalierung funktionieren kann.
		3. Verändere die Größe der GUI auf die Skalierung an unter Berücksichtigung von Randgrößen, DPI, ...
			Dabei wird an _GUI_Move nur $GUI_WIDTH und $GUI_HEIGHT übergeben, da die DPI-Größe automatisch berechnet wird.
		4. Zeige das Fenster an.

		5. Fenster ist fertig erstellt und alles ist perfekt mitskaliert
#ce
Func Example1()
	Local Const $GUI_CLIENT_WIDTH = 314
	Local Const $GUI_CLIENT_HEIGHT = 130

	Local $hGUI = GUICreate("Beispiel 1", $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT, -1, -1)

	Local $hLabel1 = GUICtrlCreateLabel("Label1", 16, 16, 36, 21)
	GUICtrlSetBkColor(-1, 0x3399FF)
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

	Local $hLabel2 = GUICtrlCreateLabel("Label2", 64, 16, 36, 21)
	GUICtrlSetBkColor(-1, 0x3399FF)
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

	Local $hLabel3 = GUICtrlCreateLabel("Label3", 112, 16, 36, 21)
	GUICtrlSetBkColor(-1, 0x3399FF)
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

	Local $hInput = GUICtrlCreateInput("Input1", 160, 16, 137, 21)
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

	Local $hButton = GUICtrlCreateButton("Schließen", 16, 48, 283, 65)
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

	GUISetState(@SW_HIDE, $hGUI)

	_GUI_Move($hGUI, -1, -1, $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT) ;Zur Skalierten Größe sizen

	GUISetState(@SW_SHOW, $hGUI)

	While Sleep(10)
		Switch GUIGetMsg()
			Case $GUI_EVENT_CLOSE, $hButton
				GUIDelete($hGUI)
				ExitLoop

		EndSwitch
	WEnd
EndFunc

#cs Beispiel 2

	Skalierbare GUI, so dass alle Controls ihr angepasstes Resizing behalten.

	Baut man eine GUI z.B. mit einer ListView und einem "OK"-Button unten, möchte man ja, dass
	die ListView beim manuellen sizen des Fensters größer wird und der OK-Button klein bleibt.

	Im Prinzip funktionierts wie das 1. Beispiel, allerdings wird nach dem _GUI_Move das Resizing zurückgesetzt
	und anschließend die GUI angezeigt.
#ce
Func Example2()
	Local Const $GUI_CLIENT_WIDTH = 314
	Local Const $GUI_CLIENT_HEIGHT = 130

	Local $hGUI = GUICreate("Beispiel 2", $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT, -1, -1, BitOR($GUI_SS_DEFAULT_GUI, $WS_SIZEBOX, $WS_THICKFRAME))

	Local $hLabel1 = GUICtrlCreateLabel("Label1", 16, 16, 36, 21)
	GUICtrlSetBkColor(-1, 0x3399FF)
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

	Local $hLabel2 = GUICtrlCreateLabel("Label2", 64, 16, 36, 21)
	GUICtrlSetBkColor(-1, 0x3399FF)
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

	Local $hLabel3 = GUICtrlCreateLabel("Label3", 112, 16, 36, 21)
	GUICtrlSetBkColor(-1, 0x3399FF)
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

	Local $hInput = GUICtrlCreateInput("Input1", 160, 16, 137, 21)
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

	Local $hButton = GUICtrlCreateButton("Schließen", 16, 48, 283, 65)
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

	GUISetState(@SW_HIDE, $hGUI)
	_GUI_Move($hGUI, -1, -1, $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT) ;Zur Skalierten Größe sizen

	GUICtrlSetResizing($hLabel1, $GUI_DOCKLEFT + $GUI_DOCKTOP + $GUI_DOCKHEIGHT)
	GUICtrlSetResizing($hLabel2, $GUI_DOCKTOP + $GUI_DOCKHEIGHT)
	GUICtrlSetResizing($hLabel3, $GUI_DOCKTOP + $GUI_DOCKHEIGHT)
	GUICtrlSetResizing($hInput, $GUI_DOCKRIGHT + $GUI_DOCKTOP + $GUI_DOCKHEIGHT)

	GUICtrlSetResizing($hButton, $GUI_DOCKLEFT + $GUI_DOCKRIGHT + $GUI_DOCKTOP + $GUI_DOCKBOTTOM)

	GUISetState(@SW_SHOW, $hGUI)

	While Sleep(10)
		Switch GUIGetMsg()
			Case $GUI_EVENT_CLOSE, $hButton
				GUIDelete($hGUI)
				ExitLoop

		EndSwitch
	WEnd
EndFunc

#cs Beispiel 3

	Hier mal ein konkreter Anwendungsfall.

	Bei Bedarf kann man auch das WM_SIZE abfangen und die Spaltenbreite des ListViews anpassen.

	Die Spaltenbreite muss man am Anfang korrekt setzen, da Windows das nicht automatisch skaliert
#ce
Func Example3()
	Local $GUI_CLIENT_WIDTH = 530
	Local $GUI_CLIENT_HEIGHT = 338

	Local $hGUI = GUICreate("Beispiel 3 - Liste alle Dateien auf dem Desktop auf", $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT, -1, -1, BitOR($GUI_SS_DEFAULT_GUI, $WS_SIZEBOX, $WS_THICKFRAME))
	Local $hFiles = GUICtrlCreateListView("Dateiname|Änderungsdatum|Größe", 8, 8, 514, 278)
	GUICtrlSendMsg(-1, $LVM_SETCOLUMNWIDTH, 0, 273 * $F_DPI_SCALEFACTOR)
	GUICtrlSendMsg(-1, $LVM_SETCOLUMNWIDTH, 1, 120 * $F_DPI_SCALEFACTOR)
	GUICtrlSendMsg(-1, $LVM_SETCOLUMNWIDTH, 2, 100 * $F_DPI_SCALEFACTOR)
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

	Local $aFiles = _FileListToArray(@DesktopDir)
	If UBound($aFiles) Then
		For $i = 1 To $aFiles[0]
			Local $aDate = FileGetTime(@DesktopDir & "\" & $aFiles[$i])
			Local $sDate = $aDate[2] & "." & $aDate[1] & "." & $aDate[0] & " - " & $aDate[3] & ":" & $aDate[4]
			GUICtrlCreateListViewItem($aFiles[$i] & "|" & $sDate & "|" & _GetFileSizeString(FileGetSize(@DesktopDir & "\" & $aFiles[$i])), $hFiles)
		Next
	EndIf

	Local $hButton = GUICtrlCreateButton("Schließen", 400, 296, 123, 33)
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

	GUISetState(@SW_HIDE, $hGUI)
	_GUI_Move($hGUI, -1, -1, $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT) ;Zur Skalierten Größe sizen

	GUICtrlSetResizing($hFiles, $GUI_DOCKLEFT + $GUI_DOCKRIGHT + $GUI_DOCKTOP + $GUI_DOCKBOTTOM)
	GUICtrlSetResizing($hButton, $GUI_DOCKRIGHT + $GUI_DOCKBOTTOM + $GUI_DOCKWIDTH + $GUI_DOCKHEIGHT)

	GUISetState(@SW_SHOW, $hGUI)

	While Sleep(10)
		Switch GUIGetMsg()
			Case $GUI_EVENT_CLOSE, $hButton
				GUIDelete($hGUI)
				ExitLoop

		EndSwitch
	WEnd
EndFunc

#cs Beispiel 4

	GUI wie ein Bild skalierbar machen.

#ce
Func Example4()
	Local $GUI_CLIENT_WIDTH = 282
	Local $GUI_CLIENT_HEIGHT = 106

	Local $hGUI = GUICreate("Beispiel 4", $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT, -1, -1, BitOR($GUI_SS_DEFAULT_GUI, $WS_SIZEBOX, $WS_THICKFRAME))

	GUICtrlCreateLabel("Counter:", 32, 34, 44, 17, $SS_CENTERIMAGE)
	Local $hCounter = GUICtrlCreateInput("", 88, 32, 161, 21)

	GUICtrlCreateLabel("Input:", 32, 58, 31, 17, $SS_CENTERIMAGE)
	Local $hInput = GUICtrlCreateInput("", 88, 56, 161, 21)

	GUISetState(@SW_HIDE, $hGUI)

	;_GUI_Move nicht nötig, da bereits _GUI_SetResizing das übernimmt.

	Local $aGUI_Controls[2] = [ $hCounter - 1, $hInput ]
	_GUI_SetResizing($hGUI, $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT, $aGUI_Controls)

	_GUI_Move($hGUI, -1, -1, $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT) ;Zentrieren

	GUISetState(@SW_SHOW, $hGUI)

	While GUIGetMsg() <> $GUI_EVENT_CLOSE
		Sleep(10)
	WEnd

	GUIDelete($hGUI)
EndFunc

#cs Beispiel 5

	GUI mit eigenem Resizing.

	Achtung Bug - UpDowns

	UpDowns buggen komischerweise rum, da das Input ja in der Höhe nicht mehr variieren kann.
	Siehe Resize-Funktionen für nähere Beschreibung

#ce
Func Example5()
	Local $GUI_CLIENT_WIDTH = 282
	Local $GUI_CLIENT_HEIGHT = 106

	Local $hGUI = GUICreate("Beispiel 5", $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT, -1, -1)

	GUICtrlCreateLabel("Counter:", 32, 34, 44, 17, $SS_CENTERIMAGE)
	Local $hCounter = GUICtrlCreateInput("", 88, 32, 161, 21 * $F_DPI_SCALEFACTOR)
	GUICtrlCreateUpdown(-1)

	GUICtrlCreateLabel("Input:", 32, 58, 31, 17, $SS_CENTERIMAGE)
	Local $hInput = GUICtrlCreateInput("", 88, 56, 161, 21)

	GUISetState(@SW_HIDE, $hGUI)

	;_GUI_Move nicht nötig, da bereits _GUI_SetResizing das übernimmt.

	Local $aGUI_Controls[2] = [ $hCounter - 1, $hInput ]
	_GUI_SetResizing($hGUI, $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT, $aGUI_Controls)

	_GUI_Move($hGUI, -1, -1, $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT) ;Zentrieren

	GUISetState(@SW_SHOW, $hGUI)

	While GUIGetMsg() <> $GUI_EVENT_CLOSE
		Sleep(10)
	WEnd

	GUIDelete($hGUI)
EndFunc

;Wieso nutzen wir nicht System-Metrics? Weil wir uns so nicht für jedes Fenster den Style merken müssen,
;stattdessen berechnen wir hier die Randgröße einfach. Viel besser.
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

#cs _GUI_Move($hGUI, $iX, $iY, $iWidth, $iHeight, $bIgnoreDPI = False)

	Diese Funktion dient dazu, die Fenster zu moven, dabei wird WinMove nochmal gewrappt, aber so das es nutzbar ist.
	WinMove platziert die Fenster nämlich nach der Fenstergröße und nicht Clientgröße.

	Bei GUICreate gibt man aber die Clientgröße an und das irritiert sehr.
	Die Funktion movet das Fenster passend zur DPI (kann man auch ausschalten), so dass man nur die ursprüngliche Größe übergeben muss.
	Außerdem berechnet sie die Ränder mit, d.h. wenn ich folgendes Beispiel habe.

	Local Const $iWidth = 200, $iHeight = 100
	Local $hGUI = GUICreate("", $iWidth, $iHeight)

	Dann kann ich sicher sein, dass mit _GUI_Move($hGUI, -1, -1, 200, 100) das Fenster die exakt selbe Größe hat,
	wie es beim erstellen der Fall war.

	bIgnoreDPI wird verwendet um das Fenster (bevor es irgendwann hochskaliert wird) in die richtige Position zu bringen,
	damit man nicht WinMove nutzen und die Ränder selber berechnen muss.
#ce
Func _GUI_Move($hGUI, $iX, $iY, $iWidth, $iHeight, $bIgnoreDPI = False)
	Local $aBorders = _GUI_GetBorderSize($hGUI)

	If $iX = -1 Then $iX = (@DesktopWidth - $iWidth * ($bIgnoreDPI ? 1 : $F_DPI_SCALEFACTOR) - $aBorders[0]) / 2
	If $iY = -1 Then $iY = (@DesktopHeight - $iHeight * ($bIgnoreDPI ? 1 : $F_DPI_SCALEFACTOR) - $aBorders[1]) / 2

	If Not $bIgnoreDPI Then
		$iWidth = $iWidth * $F_DPI_SCALEFACTOR
		$iHeight = $iHeight * $F_DPI_SCALEFACTOR
	EndIf

	$iWidth += $aBorders[0]
	$iHeight += $aBorders[1]

	Return WinMove( _
		$hGUI, _
		"", _
		$iX, _
		$iY, _
		$iWidth, _
		$iHeight _
	)
EndFunc

#cs _GUI_SetResizing($hGUI, $iWidth, $iHeight, $aControls)

	Das Kernstück für die perfekte DPI-Skalierung.

	Wenn alle Controls auf einer GUI platziert sind sollte _GUI_SetResizing aufgerufen werden.

	_GUI_SetResizing macht dann folgendes:

		1. Size das Fenster auf originale Größe zurück (ignoriere DPI-Skalierung)
			(falls man Controls seitenartig darstellen möchte wie es bei den PassIt2 Einstellungen der Fall ist)
		2. Setze das Resizing aller Controls auf $GUI_DOCKAUTO, so dass sie mitskalieren
		3. _GUI_Move das Fenster zur korrekten skalierten Größe, $GUI_DOCKAUTO vergrößert die Controls dabei
		4. Setze die Controls auf ihr eigentliches Resizeverhalten zurück (sofern das übergeben wurde) damit
			man GUIs auch verschieden skalierbar machen kann (Beispiel: PassIt2 - TransferGUI)

	GUICtrlUpDown machen dabei ERHEBLICHE Probleme, bitte die Kommentare innerhalb der Funktion lesen!
#ce
Func _GUI_SetResizing($hGUI, $iWidth, $iHeight, $aControls)
	;Für die SettingsGUI wichtig, wenn wir das Fenster nicht wieder kleiner machen werden die Elemente auf der großen GUI
	;erzeugt und skalieren erst danach mit, obwohl sie schon vorher hochskaliert werden sollen.
	;Der letzte Parameter weist unsere Funktion an die DPI zu ignorieren und sie zur angegeben Größe + Rand zu verkleinern.
	_GUI_Move($hGUI, Default, Default, $iWidth, $iHeight, True)

	;$aControls kann entweder ein 2D-Array sein, da ist [n][0] = hControl und [n][1] = ResizeParameter
	;oder eindimensional mit [0] = hControlBegin und [1] = hControlEnd welche alle $GUI_DOCKAUTO bekommen.

	; ===== ACHTUNG ~ WINDOWS BUG ~ ACHTUNG =====
	;	Wenn man das Input resized (während es versteckt ist UND ein UpDown hat!), resized das ganze nicht korrekt.
	;	Wir dürfen nur das Resizing vom UpDown setzen und nicht vom Input.
	;
	;	PassIt2 ist so aufgebaut, dass die UpDown-Controls direkt nach den Inputs erstellt werden,
	;	und da AutoIt glücklicherweise eine aufsteigende Numeration von Controls verwendet,
	;	können wir einfach +1 rechnen und zu schauen ob das nächste ein UpDown sein könnte mit _WinAPI_GetClassName.
	;
	;	Natürlich könnten wir auch einfach den zweiten If-Zweig nehmen und alle bis auf die Inputs mit UpDowns übergeben,
	;	aber so spart das Zeilen und ist einfacher, da das ja ein allgemeines Problem ist.
	;
	;	Sehr wichtig:
	;		Die Inputs mit UpDowns müssen übrigens schon vorher mit $F_DPI_SCALEFACTOR erstellt werden, da sie sonst falsch mitskalieren.
	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

Func _GetFileSizeString($iSize)
	If $iSize < 1024 Then
		Return $iSize & " B"
	ElseIf $iSize < 1024 * 1024 Then
		Return StringFormat("%.2f", $iSize / 1024) & " KB"
	ElseIf $iSize < 1024 * 1024 * 1024 Then
		Return StringFormat("%.2f", $iSize / 1024 / 1024) & " MB"
	Else
		Return StringFormat("%.2f", $iSize / 1024 / 1024 / 1024) & " GB"
	EndIf
EndFunc