Jump to content

Resizable status bar without SBARS_SIZEGRIP


Go to solution Solved by pixelsearch,

Recommended Posts

Posted

For what it's worth, here is what ended up working for me:

;==============================================
Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam
    Local Static $bSizeboxOffScreen = False

    If $hWnd = $g_hGUI Then
        Local $aPos = WinGetPos($g_hSizebox)
        If $aPos[0] + $aPos[2] > @DesktopWidth Or $aPos[1] + $aPos[3] > @DesktopHeight Then
            $bSizeboxOffScreen = True
        Else
            If $bSizeboxOffScreen Then
                ; sizebox was off-screen but is back in range now, redraw GUI
                _WinAPI_RedrawWindow($g_hGui)
                $bSizeboxOffScreen = False
            EndIf
        EndIf
        _WinAPI_RedrawWindow($g_hSizebox)
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE

Keep in mind that this may or may not work in a multi-monitor setup. However, multi-monitor setups require quite a bit different DPI scaling settings and so on. So this is quite basic but it works 100% of the time for me. :)

Posted

I just wanted to share an updated version of the CreateDots function which matches the Windows gripper dots identically. The dot size, dot spacing and placement on the statusbar is a perfect match now at 100%, 125%, 150% and 175% DPI scaling. Please note, my monitor does not do 200% scaling or higher and therefore 200% and higher are untested.

The dots are so accurate that I tested by removing the ownerdrawn parts and applied light mode Explorer theme to the statusbar which shows the default gripper dots. I then used System Informer to hide/unhide the "Scrollbar" window and everything was perfect at 100%, 125%, 150% and 175% DPI scaling. I am a little bit frustrated that I can't test anything higher.

Changes:

; set base DPI scale value and apply DPI
Global $iDPI = 1
ApplyDPI()
; DPI needs to be applied before the Example() function
Func ApplyDPI()
    ; apply System DPI awareness
    DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext" , "HWND", "DPI_AWARENESS_CONTEXT" -2)
    ; update DPI scale value based on DPI scaling already applied to AutoIt hidden window
    $iDPI = Round(_WinAPI_GetDpiForWindow(WinGetHandle(AutoItWinGetTitle())) / 96, 2)
    If @error Then $iDPI = 1
EndFunc

...

;==============================================
Func CreateDots($iWidth, $iHeight, $iBackgroundColor, $iDotsColor) ; Andreik
    Local $iDotSize, $iDotSpace, $iDotFrame
    Switch $iDPI
        Case 1      ; 100% scaling
            $iDotSize = 2
            $iDotSpace = $iDotSize - 1  ; ensures that dots have only 1 pixel of space between
            $iDotFrame = -1             ; controls the distance from frame
            Local $a[6][2] = [[3,9], [3,6], [3,3], [6,6], [6,3], [9,3]]
        Case 1.25   ; 125% scaling
            $iDotSize = 3
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case 1.5    ; 150% scaling
            $iDotSize = 3
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case 1.75   ; 175% scaling
            $iDotSize = 3
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case 2      ; 200% scaling
            $iDotSize = 4
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case Else
            $iDotSize = 1
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
    EndSwitch
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    Local $hBrush = _GDIPlus_BrushCreateSolid($iDotsColor)
    _GDIPlus_GraphicsClear($hGraphics, $iBackgroundColor)
    For $i = 0 To UBound($a) - 1
        _GDIPlus_GraphicsFillRect($hGraphics, $iWidth - ($iDotSpace * $a[$i][0]) + $iDotFrame, $iHeight - ($iDotSpace * $a[$i][1]) + $iDotFrame, $iDotSize, $iDotSize, $hBrush)
    Next
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hGraphics)
    Return $hBitmap
EndFunc   ;==>CreateDots

...

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

 

And here is the full working script:

#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <GuiStatusBar.au3>
#include <StaticConstants.au3>
#include <WinAPIGdi.au3>
#include <WinAPIRes.au3>
#include <WinAPISysWin.au3>
#include <WinAPITheme.au3>
#include <WindowsConstants.au3>

Opt("MustDeclareVars", 1)

Global $g_hGui, $g_hSizebox, $g_hOldProc, $g_hStatus, $g_iHeight, $g_aText, $g_aRatioW, $g_iBkColor, $g_iTextColor, $g_hDots
; Global $g_hBrush ; no need, as _GUICtrlStatusBar_SetBkColor() placed after _WinAPI_SetWindowTheme() does the job correctly

; set base DPI scale value and apply DPI
Global $iDPI = 1
ApplyDPI()

Func ApplyDPI()
    ; apply System DPI awareness
    DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext" , "HWND", "DPI_AWARENESS_CONTEXT" -2)
    ; update DPI scale value based on DPI scaling already applied to AutoIt hidden window
    $iDPI = Round(_WinAPI_GetDpiForWindow(WinGetHandle(AutoItWinGetTitle())) / 96, 2)
    If @error Then $iDPI = 1
EndFunc

Example()

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

    _GDIPlus_Startup()

    Local Const $SBS_SIZEBOX = 0x08, $SBS_SIZEGRIP = 0x10
    Local $iW = 300, $iH = 100
    Dim $g_iBkColor = 0x383838, $g_iTextColor = 0xFFFFFF

    $g_hGui = GUICreate("Resize corner", $iW, $iH, -1, -1, $WS_OVERLAPPEDWINDOW)
    GUISetBkColor($g_iBkColor)

    ;-----------------
    ; Create a sizebox window (Scrollbar class) BEFORE creating the StatusBar control
    $g_hSizebox = _WinAPI_CreateWindowEx(0, "Scrollbar", "", $WS_CHILD + $WS_VISIBLE + $SBS_SIZEBOX, _
        0, 0, 0, 0, $g_hGui) ; $SBS_SIZEBOX or $SBS_SIZEGRIP

    ; Subclass the sizebox (by changing the window procedure associated with the Scrollbar class)
    Local $hProc = DllCallbackRegister('ScrollbarProc', 'lresult', 'hwnd;uint;wparam;lparam')
    $g_hOldProc = _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, DllCallbackGetPtr($hProc))

    Local $hCursor = _WinAPI_LoadCursor(0, $OCR_SIZENWSE)
    _WinAPI_SetClassLongEx($g_hSizebox, -12, $hCursor) ; $GCL_HCURSOR = -12

    ; $g_hBrush = _WinAPI_CreateSolidBrush($g_iBkColor)

    ;-----------------
    $g_hStatus = _GUICtrlStatusBar_Create($g_hGui, -1, "", $WS_CLIPSIBLINGS) ; ClipSiblings style +++
    Local $aParts[3] = [90, 180, 280]
    If $aParts[Ubound($aParts) - 1] = -1 Then $aParts[Ubound($aParts) - 1] = $iW ; client width size
    _MyGUICtrlStatusBar_SetParts($g_hStatus, $aParts)

    Dim $g_aText[Ubound($aParts)] = ["Part 0", "Part 1", "Part 2"]
    Dim $g_aRatioW[Ubound($aParts)]
    For $i = 0 To UBound($g_aText) - 1
        _GUICtrlStatusBar_SetText($g_hStatus, "", $i, $SBT_OWNERDRAW)
        ; _GUICtrlStatusBar_SetText($g_hStatus, "", $i, $SBT_OWNERDRAW + $SBT_NOBORDERS) ; interesting ?
        $g_aRatioW[$i] = $aParts[$i] / $iW
    Next

    Local $idChangeText = GUICtrlCreateLabel("Change Text", 110, 25, 80, 30, $SS_CENTER + $SS_CENTERIMAGE), $iInc
    GUICtrlSetColor(-1, 0xFFFF00) ; yellow

    ; to allow the setting of StatusBar BkColor at least under Windows 10
    _WinAPI_SetWindowTheme($g_hStatus, "", "")

    ; Set status bar background color
    _GUICtrlStatusBar_SetBkColor($g_hStatus, $g_iBkColor)

    $g_iHeight = _GUICtrlStatusBar_GetHeight($g_hStatus) + 3 ; change the constant (+3) if necessary
    $g_hDots = CreateDots($g_iHeight, $g_iHeight, 0xFF000000 + $g_iBkColor, 0xFF000000 + $g_iTextColor)

    GUIRegisterMsg($WM_SIZE, "WM_SIZE")
    GUIRegisterMsg($WM_MOVE, "WM_MOVE")
    GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM")
    GUISetState()

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $idChangeText
                $iInc += 1
                For $i = 0 To UBound($g_aText) - 1
                    $g_aText[$i] = "Part " & $i & " : Inc " & $iInc
                Next
                _WinAPI_RedrawWindow($g_hStatus)
        EndSwitch
    WEnd

    _GDIPlus_BitmapDispose($g_hDots)
    _GUICtrlStatusBar_Destroy($g_hStatus)
    _WinAPI_DestroyCursor($hCursor)
    ; _WinAPI_DeleteObject($g_hBrush)
    _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, $g_hOldProc)
    DllCallbackFree($hProc)
    _GDIPlus_Shutdown()
EndFunc   ;==>Example

;==============================================
Func ScrollbarProc($hWnd, $iMsg, $wParam, $lParam) ; Andreik

    If $iMsg = $WM_PAINT Then
        Local $tPAINTSTRUCT
        Local $hDC = _WinAPI_BeginPaint($hWnd, $tPAINTSTRUCT)
        Local $iWidth = DllStructGetData($tPAINTSTRUCT, 'rPaint', 3) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 1)
        Local $iHeight = DllStructGetData($tPAINTSTRUCT, 'rPaint', 4) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 2)
        Local $hGraphics = _GDIPlus_GraphicsCreateFromHDC($hDC)
        _GDIPlus_GraphicsDrawImageRect($hGraphics, $g_hDots, 0, 0, $iWidth, $iHeight)
        _GDIPlus_GraphicsDispose($hGraphics)
        _WinAPI_EndPaint($hWnd, $tPAINTSTRUCT)
        Return 0
    EndIf
    Return _WinAPI_CallWindowProc($g_hOldProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>ScrollbarProc

;==============================================
Func CreateDots($iWidth, $iHeight, $iBackgroundColor, $iDotsColor) ; Andreik
    Local $iDotSize, $iDotSpace, $iDotFrame
    Switch $iDPI
        Case 1      ; 100% scaling
            $iDotSize = 2
            $iDotSpace = $iDotSize - 1  ; ensures that dots have only 1 pixel of space between
            $iDotFrame = -1             ; controls the distance from frame
            Local $a[6][2] = [[3,9], [3,6], [3,3], [6,6], [6,3], [9,3]]
        Case 1.25   ; 125% scaling
            $iDotSize = 3
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case 1.5    ; 150% scaling
            $iDotSize = 3
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case 1.75   ; 175% scaling
            $iDotSize = 3
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case 2      ; 200% scaling
            $iDotSize = 4
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case Else
            $iDotSize = 1
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
    EndSwitch
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    Local $hBrush = _GDIPlus_BrushCreateSolid($iDotsColor)
    _GDIPlus_GraphicsClear($hGraphics, $iBackgroundColor)
    For $i = 0 To UBound($a) - 1
        _GDIPlus_GraphicsFillRect($hGraphics, $iWidth - ($iDotSpace * $a[$i][0]) + $iDotFrame, $iHeight - ($iDotSpace * $a[$i][1]) + $iDotFrame, $iDotSize, $iDotSize, $hBrush)
    Next
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hGraphics)
    Return $hBitmap
EndFunc   ;==>CreateDots

;==============================================
Func CreateDots_old($iWidth, $iHeight, $iBackgroundColor, $iDotsColor) ; Andreik

    Local $iDotSize = Int($iHeight / 10)
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    Local $hBrush = _GDIPlus_BrushCreateSolid($iDotsColor)
    _GDIPlus_GraphicsClear($hGraphics, $iBackgroundColor)
    Local $a[6][2] = [[2,6], [2,4], [2,2], [4,4], [4,2], [6,2]]
    For $i = 0 To UBound($a) - 1
        _GDIPlus_GraphicsFillRect($hGraphics, $iWidth - $iDotSize * $a[$i][0], $iHeight - $iDotSize * $a[$i][1], $iDotSize, $iDotSize, $hBrush)
    Next
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hGraphics)
    Return $hBitmap
EndFunc   ;==>CreateDots_old

;==============================================
Func _MyGUICtrlStatusBar_SetParts($hWnd, $aPartEdge) ; Pixelsearch

    If Not IsArray($aPartEdge) Then Return False
    Local $iParts = UBound($aPartEdge)
    Local $tParts = DllStructCreate("int[" & $iParts & "]")
    For $i = 0 To $iParts - 1
        DllStructSetData($tParts, 1, $aPartEdge[$i], $i + 1)
    Next
    DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $hWnd, "uint", $SB_SETPARTS, "wparam", $iParts, "struct*", $tParts)
    _GUICtrlStatusBar_Resize($hWnd)
    Return True
EndFunc   ;==>_MyGUICtrlStatusBar_SetParts

;==============================================
Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam) ; Pixelsearch
    #forceref $iMsg, $wParam, $lParam

    If $hWnd = $g_hGUI Then
        Local Static $bIsSizeBoxShown = True
        Local $aSize = WinGetClientSize($g_hGui)
        Local $aGetParts = _GUICtrlStatusBar_GetParts($g_hStatus)
        Local $aParts[$aGetParts[0]]
        For $i = 0 To $aGetParts[0] - 1
            $aParts[$i] = Int($aSize[0] * $g_aRatioW[$i])
        Next
        If BitAND(WinGetState($g_hGui), $WIN_STATE_MAXIMIZED) Then
            _GUICtrlStatusBar_SetParts($g_hStatus, $aParts) ; set parts until GUI right border (AutoIt function forces it)
            _WinAPI_ShowWindow($g_hSizebox, @SW_HIDE)
            $bIsSizeBoxShown = False
        Else
            If $g_aRatioW[UBound($aParts) -1] <> 1 Then $aParts[UBound($aParts) -1] = $aSize[0] - $g_iHeight ; right size of right part stuck on sizebox (no gap)
            _MyGUICtrlStatusBar_SetParts($g_hStatus, $aParts) ; set parts until sizebox (see preceding line) or until GUI right border
            WinMove($g_hSizebox, "", $aSize[0] - $g_iHeight, $aSize[1] - $g_iHeight, $g_iHeight, $g_iHeight)
            If Not $bIsSizeBoxShown Then
                _WinAPI_ShowWindow($g_hSizebox, @SW_SHOW)
                $bIsSizeBoxShown = True
            EndIf
        EndIf
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_SIZE

;==============================================
Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam

    If $hWnd = $g_hGUI Then
        _WinAPI_RedrawWindow($g_hSizebox)
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE

;==============================================
Func WM_DRAWITEM($hWnd, $iMsg, $wParam, $lParam) ; Kafu
    #forceref $hWnd, $iMsg, $wParam

    Local Static $tagDRAWITEM = "uint CtlType;uint CtlID;uint itemID;uint itemAction;uint itemState;hwnd hwndItem;handle hDC;long rcItem[4];ulong_ptr itemData"
    Local $tDRAWITEM = DllStructCreate($tagDRAWITEM, $lParam)
    If $tDRAWITEM.hwndItem <> $g_hStatus Then Return $GUI_RUNDEFMSG ; only process the statusbar

    Local $itemID = $tDRAWITEM.itemID ; status bar part number : 0 if simple bar (or 0, 1, 2... if multi parts)
    Local $hDC = $tDRAWITEM.hDC
    Local $tRect = DllStructCreate("long left;long top;long right;long bottom", DllStructGetPtr($tDRAWITEM, "rcItem"))
    ; _WinAPI_FillRect($hDC, DllStructGetPtr($tRect), $g_hBrush) ; backgound color
    _WinAPI_SetTextColor($hDC, $g_iTextColor) ; text color
    _WinAPI_SetBkMode($hDC, $TRANSPARENT)
    DllStructSetData($tRect, "top", $tRect.top + 1)
    DllStructSetData($tRect, "left", $tRect.left + 1)
    _WinAPI_DrawText($hDC, $g_aText[$itemID], $tRect, $DT_LEFT)

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_DRAWITEM

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

 

Posted (edited)

with sacrifice ?  :)
 

#include <GUIConstantsEx.au3>
#include <GuiStatusBar.au3>
#include <StaticConstants.au3>
#include <WinAPIGdi.au3>
#include <WindowsConstants.au3>
#include <WinAPITheme.au3>


Opt("MustDeclareVars", 1)

Global $g_hGui, $g_idSizeGrip, $g_hStatus, $g_iHeight
Global $g_iBkColor = 0x383838, $g_iTextColor = 0xFFFFFF
Global $iDPI = 1
Global $g_iGripW = _WinAPI_GetSystemMetrics($SM_CXVSCROLL)

; Initialize arrays properly at start
Global $g_aText[3] = ["Part 0", "Part 1", "Part 2"]
Global $g_aRatioW[3] = [0.3, 0.6, 1.0] ; Proportions for resizing

ApplyDPI()

Example()

;---------------------------------------------------------------------------------------
Func Example() ; Create Main GUI
    Local $iW = 400, $iH = 200

    ; Create Main GUI
    $g_hGui = GUICreate("Resize Corner ", $iW, $iH, -1, -1, $WS_OVERLAPPEDWINDOW)
    GUISetBkColor($g_iBkColor)

    ; Create StatusBar
    _Create_StatusBar($iW, $iH)

    Local $idChangeText = GUICtrlCreateLabel("Change Text", 110, 25, 80, 30, $SS_CENTER + $SS_CENTERIMAGE), $iInc
    GUICtrlSetColor(-1, 0xFFFF00) ; yellow
    GUICtrlSetResizing(-1, $GUI_DOCKSIZE)

    ; Register Messages for real-time response
    GUIRegisterMsg($WM_SIZE, "WM_SIZE")
    GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM")

    GUISetState(@SW_SHOW)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $idChangeText
                $iInc += 1
                For $i = 0 To UBound($g_aText) - 1
                    $g_aText[$i] = "Part " & $i & " : Inc " & $iInc
                Next
                _WinAPI_RedrawWindow($g_hGui)

        EndSwitch
    WEnd
EndFunc   ;==>Example
;---------------------------------------------------------------------------------------
Func _Create_StatusBar($iW, $iH) ; Create StatusBar

    $g_hStatus = _GUICtrlStatusBar_Create($g_hGui, -1, "", $WS_CLIPSIBLINGS)
    _WinAPI_SetWindowTheme($g_hStatus, "", "")
    _GUICtrlStatusBar_SetBkColor($g_hStatus, $g_iBkColor)

    ; Initial Parts setup
    Local $aInitialParts[3] = [Int($iW * 0.3), Int($iW * 0.6), $iW - $g_iGripW]
    _MyGUICtrlStatusBar_SetParts($g_hStatus, $aInitialParts)

    For $i = 0 To UBound($g_aText) - 1
        _GUICtrlStatusBar_SetText($g_hStatus, "", $i, $SBT_OWNERDRAW)
    Next

    ; Create the Grip Label 0x2591(░) or 0x2592(▒) or 0x2593(▓)
    $g_iHeight = _GUICtrlStatusBar_GetHeight($g_hStatus)
    $g_idSizeGrip = GUICtrlCreateLabel(ChrW(0x2591), $iW - $g_iGripW, $iH - $g_iHeight - 3, $g_iGripW, $g_iHeight + 3)

    GUICtrlSetFont(-1, $g_iHeight) ;, 400, 0, "Lucida Console")
    GUICtrlSetColor(-1, $g_iTextColor)
    GUICtrlSetBkColor(-1, $g_iBkColor)
;~  GUICtrlSetCursor(-1, 12) ; IDC_SIZENWSE ; ??
    GUICtrlSetResizing(-1, $GUI_DOCKSIZE + $GUI_DOCKRIGHT + $GUI_DOCKBOTTOM) ; Defines the resizing method
EndFunc   ;==>_Create_StatusBar
;---------------------------------------------------------------------------------------
Func _MyGUICtrlStatusBar_SetParts($hWnd, $aPartEdge) ; Function to set StatusBar parts
    Local $iParts = UBound($aPartEdge)
    Local $tParts = DllStructCreate("int[" & $iParts & "]")
    For $i = 0 To $iParts - 1
        DllStructSetData($tParts, 1, $aPartEdge[$i], $i + 1)
    Next
    DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $hWnd, "uint", $SB_SETPARTS, "wparam", $iParts, "struct*", $tParts)
    Return True
EndFunc   ;==>_MyGUICtrlStatusBar_SetParts
;---------------------------------------------------------------------------------------
Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam) ; This function handles the resizing in real-time
    #forceref $iMsg, $wParam, $lParam

    If $hWnd = $g_hGui Then
        ; Get new Client Area dimensions
        Local $iW = BitAND($lParam, 0xFFFF)
        Local $iH = BitShift($lParam, 16)

        ; Resize StatusBar parts
        Local $aParts[3]
        $aParts[0] = Int($iW * $g_aRatioW[0])
        $aParts[1] = Int($iW * $g_aRatioW[1])
        $aParts[2] = $iW - $g_iGripW ; Ensure there's a $g_iGripW gap for the grip character

        _MyGUICtrlStatusBar_SetParts($g_hStatus, $aParts)
        _GUICtrlStatusBar_Resize($g_hStatus) ; Force the status bar to redraw itself

        ; Move the Grip Label precisely using WinAPI for zero lag
        Local $hGrip = GUICtrlGetHandle($g_idSizeGrip)
        _WinAPI_MoveWindow($hGrip, $iW - $g_iGripW, $iH - $g_iHeight - 3, $g_iGripW, $g_iHeight + 3, True)
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_SIZE
;---------------------------------------------------------------------------------------
Func WM_DRAWITEM($hWnd, $iMsg, $wParam, $lParam) ; Custom Text Drawing
    #forceref $hWnd, $iMsg, $wParam
    Local $tDRAWITEM = DllStructCreate("uint CtlType;uint CtlID;uint itemID;uint itemAction;uint itemState;hwnd hwndItem;handle hDC;long rcItem[4];ulong_ptr itemData", $lParam)

    If DllStructGetData($tDRAWITEM, "hwndItem") = $g_hStatus Then
        Local $hDC = DllStructGetData($tDRAWITEM, "hDC")
        Local $itemID = DllStructGetData($tDRAWITEM, "itemID")

        _WinAPI_SetTextColor($hDC, $g_iTextColor)
        _WinAPI_SetBkMode($hDC, 1) ; TRANSPARENT

        Local $tRect = DllStructCreate("long left;long top;long right;long bottom")
        DllStructSetData($tRect, "left", DllStructGetData($tDRAWITEM, "rcItem", 1) + 6)
        DllStructSetData($tRect, "top", DllStructGetData($tDRAWITEM, "rcItem", 2) + 3)
        DllStructSetData($tRect, "right", DllStructGetData($tDRAWITEM, "rcItem", 3))
        DllStructSetData($tRect, "bottom", DllStructGetData($tDRAWITEM, "rcItem", 4))

        _WinAPI_DrawText($hDC, $g_aText[$itemID], $tRect, $DT_LEFT)
        Return True
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_DRAWITEM
;---------------------------------------------------------------------------------------
Func ApplyDPI() ; Apply DPI
    DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "HWND", -2)
    Local $hAutoIt = WinGetHandle(AutoItWinGetTitle())
    $iDPI = _WinAPI_GetDpiForWindow($hAutoIt) / 96
    If @error Or $iDPI <= 0 Then $iDPI = 1
EndFunc   ;==>ApplyDPI
;---------------------------------------------------------------------------------------
Func _WinAPI_GetDpiForWindow($hWnd) ; Get Dpi
    Local $aResult = DllCall("user32.dll", "uint", "GetDpiForWindow", "hwnd", $hWnd)
    If @error Then Return 96
    Return $aResult[0]
EndFunc   ;==>_WinAPI_GetDpiForWindow
;---------------------------------------------------------------------------------------

 

Edited by ioa747
chance to $SM_CXVSCROLL

I know that I know nothing

Posted
16 hours ago, ioa747 said:

with sacrifice ?  :)

This is a really cool idea. Thanks for sharing. :)

Using fonts for the texture is nice because it's quite easy but also scales very well.

7 hours ago, argumentum said:

...all you need is an 8k monitor. If the wife ask about the expense, you say "honey: was for science", and she'll say "ok, that makes sense" and you'll be off the hook :D

If I ask for that I may be sleeping on the couch. 🙃

I ended up extracting all of the gripper images (PNG) from the aero.msstyles file for each of the resolutions to confirm my initial measurements but also to get the measurements for 200% and higher scaling.

Although that makes me wonder now... I wonder if I could just add those PNG images (1 KB each x 8 images) to the compiled binary, load them and place over the same area of the statusbar that we are drawing over with the CreateDots function. I would probably convert them to ICO so that they can be 1 single file with a layer for each DPI scale.

Posted (edited)
23 hours ago, WildByDesign said:
        Case 2      ; 200% scaling
            $iDotSize = 4
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case Else

8k can easily use 500%. Thinking of after 200% "screw you buddy", ain't nice.
As long as odd percentages look good ( 125% & 175% ) in your monitor, it should look the same in greater scaling like 225%

All tho, if you could get yourself a 4k monitor ( they are quite inexpensive nowadays compared to some years ago ) it would be good.

 

Edited by argumentum
English

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted
On 1/3/2026 at 10:22 PM, WildByDesign said:
$iDPI = Round(_WinAPI_GetDpiForWindow(WinGetHandle(AutoItWinGetTitle())) / 96, 2)

I was actually quite proud of myself yesterday for the simple creativity of this line. Getting the DPI from the hidden AutoIt window works well and it is easier to get the handle for it with a small amount of code. This way, you can have the DPI scale before the GUI launches.

However, this proved flawed. It worked yesterday. But I noticed today that it was working only about 5-10% of the time. The reason for that is quite simple: The hidden AutoIt window isn't always created yet at that point.

I noticed that if I put Sleep(50) above that line, it works every time. But timing changes like that aren't always great because it may not work the same on all hardware.

I'm going to fix the DPI stuff today (already have on my machine) and I will finish the CreateDots creation for 200% scaling and higher. Then I will share an updated script later. :)

Posted (edited)

Can anyone with the ability to test at 200% scaling and higher please test something for me?

I made it so that the GUI has a button to hide/show the fake sizebox to make it easier to confirm whether the sizebox dot size, spacing and alignment is correct or not. When you hide the fake sizebox, it will show the genuine Windows sizebox/sizegrip for comparison.

I have already confirmed 100%, 125%, 150% and 175%.

I've added 200%, 250%, 300% and 400% and need those confirmed, if possible.

What I've been doing is simply doing a screenshot with the fake sizebox showing and a screenshot of the fake sizebox hidden. Then I put them into an image editing program on two different layers, zoom in and show/hide one of the layers to see whether or not the dot size, spacing and alignment matches. Or if you share the screenshots here, I can compare and make any adjustments to the CreateDots function.

Once everything is working right, I should be able to combine many of the lines in the CreateDots function to make it more compact.

Thank you! :)

EDIT: I should also mention that the GUI does not react to DPI changes dynamically. So when changing to another DPI scale to test, you will have to close the GUI and run the script again.

#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <GuiStatusBar.au3>
#include <StaticConstants.au3>
#include <WinAPIGdi.au3>
#include <WinAPIRes.au3>
#include <WinAPISysWin.au3>
#include <WinAPITheme.au3>
#include <WindowsConstants.au3>

Opt("MustDeclareVars", 1)

Global $g_hGui, $g_hSizebox, $g_hOldProc, $g_hStatus, $g_iHeight, $g_aText, $g_aRatioW, $g_iBkColor, $g_iTextColor, $g_hDots
Global $g_hBrush

; set base DPI scale value and apply DPI
Global $idChangeText
Global $DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2
Global $iDPI = 1
ApplyDPI()

Func ApplyDPI()
    ; apply System DPI awareness and calculate factor
    _WinAPI_SetThreadDpiAwarenessContext($DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)
    If Not @error Then
        $iDPI = Round(_WinAPI_GetDpiForSystem() / 96, 2)
        If @error Then $iDPI = 1
    Else
        $iDPI = 1
    EndIf

    ; simulate 200% scaling for sizebox
    ;$iDPI = 2
    ConsoleWrite("DPI scale: " & $iDPI & @CRLF)
EndFunc

Example()

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

    _GDIPlus_Startup()

    Local Const $SBS_SIZEBOX = 0x08, $SBS_SIZEGRIP = 0x10
    Local $iW = 320 * $iDPI, $iH = 100 * $iDPI
    Dim $g_iBkColor = _WinAPI_SwitchColor(_WinAPI_GetSysColor($COLOR_BTNFACE)), $g_iTextColor = 0x000000

    $g_hGui = GUICreate("Resize corner", $iW, $iH, -1, -1, $WS_OVERLAPPEDWINDOW)
    GUISetBkColor($g_iBkColor)

    ;-----------------
    ; Create a sizebox window (Scrollbar class) BEFORE creating the StatusBar control
    $g_hSizebox = _WinAPI_CreateWindowEx(0, "Scrollbar", "", $WS_CHILD + $WS_VISIBLE + $SBS_SIZEBOX, _
        0, 0, 0, 0, $g_hGui) ; $SBS_SIZEBOX or $SBS_SIZEGRIP

    ; Subclass the sizebox (by changing the window procedure associated with the Scrollbar class)
    Local $hProc = DllCallbackRegister('ScrollbarProc', 'lresult', 'hwnd;uint;wparam;lparam')
    $g_hOldProc = _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, DllCallbackGetPtr($hProc))

    Local $hCursor = _WinAPI_LoadCursor(0, $OCR_SIZENWSE)
    _WinAPI_SetClassLongEx($g_hSizebox, -12, $hCursor) ; $GCL_HCURSOR = -12

    $g_hBrush = _WinAPI_CreateSolidBrush($g_iBkColor)

    ;-----------------
    $g_hStatus = _GUICtrlStatusBar_Create($g_hGui, -1, "", $WS_CLIPSIBLINGS) ; ClipSiblings style +++
    Local $aParts[3] = [90, 180, -1]
    If $aParts[Ubound($aParts) - 1] = -1 Then $aParts[Ubound($aParts) - 1] = $iW ; client width size
    _MyGUICtrlStatusBar_SetParts($g_hStatus, $aParts)

    Dim $g_aText[Ubound($aParts)] = [" ", " ", " "]
    Dim $g_aRatioW[Ubound($aParts)]
    For $i = 0 To UBound($g_aText) - 1
        _GUICtrlStatusBar_SetText($g_hStatus, "", $i, $SBT_OWNERDRAW + $SBT_NOBORDERS)
        $g_aRatioW[$i] = $aParts[$i] / $iW
    Next

    $idChangeText = GUICtrlCreateButton("Hide Fake Sizebox", 110 * $iDPI, 25 * $iDPI, -1, -1)

    ; ensure that sizebox has Explorer theme with gripper dots
    _WinAPI_SetWindowTheme($g_hStatus, "Explorer")

    $g_iHeight = _GUICtrlStatusBar_GetHeight($g_hStatus) + 0 ; change the constant (+3) if necessary
    $g_hDots = CreateDots($g_iHeight, $g_iHeight, 0xFF000000 + $g_iBkColor, 0xFF000000 + $g_iTextColor)

    GUIRegisterMsg($WM_SIZE, "WM_SIZE")
    GUIRegisterMsg($WM_MOVE, "WM_MOVE")
    GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM")

    ; get rid of the focus rectangle dots on button
    GUICtrlSendMsg($idChangeText, $WM_CHANGEUISTATE, 65537, 0)

    ; set title with DPI scaling info
    WinSetTitle($g_hGui, "", $iDPI * 100 & "% DPI Scale")

    GUISetState()

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $idChangeText
                _ShowHideSizebox()
        EndSwitch
    WEnd

    _GDIPlus_BitmapDispose($g_hDots)
    _GUICtrlStatusBar_Destroy($g_hStatus)
    _WinAPI_DestroyCursor($hCursor)
    _WinAPI_DeleteObject($g_hBrush)
    _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, $g_hOldProc)
    DllCallbackFree($hProc)
    _GDIPlus_Shutdown()
EndFunc   ;==>Example

;==============================================
Func ScrollbarProc($hWnd, $iMsg, $wParam, $lParam) ; Andreik

    If $iMsg = $WM_PAINT Then
        Local $tPAINTSTRUCT
        Local $hDC = _WinAPI_BeginPaint($hWnd, $tPAINTSTRUCT)
        Local $iWidth = DllStructGetData($tPAINTSTRUCT, 'rPaint', 3) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 1)
        Local $iHeight = DllStructGetData($tPAINTSTRUCT, 'rPaint', 4) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 2)
        Local $hGraphics = _GDIPlus_GraphicsCreateFromHDC($hDC)
        _GDIPlus_GraphicsDrawImageRect($hGraphics, $g_hDots, 0, 0, $iWidth, $iHeight)
        _GDIPlus_GraphicsDispose($hGraphics)
        _WinAPI_EndPaint($hWnd, $tPAINTSTRUCT)
        Return 0
    EndIf
    Return _WinAPI_CallWindowProc($g_hOldProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>ScrollbarProc

;==============================================
Func CreateDots($iWidth, $iHeight, $iBackgroundColor, $iDotsColor) ; Andreik
    Local $iDotSize, $iDotSpace, $iDotFrame
    Switch $iDPI
        ;       Dot Size    Spacing     From Right  From Bottom Confirmed
        ; 100%      2           1           2           2           yes
        ; 125%      3           1           2           2           yes
        ; 150%      3           1           2           2           yes
        ; 175%      3           1           2           2           not existing
        ; 200%      4           2           4           4           ?
        ; 250%      5           2           5           5           ?
        ; 300%      6           3           6           6           ?
        ; 400%      8           4           8           8           ?
        Case 1      ; 100% scaling
            $iDotSize = 2
            $iDotSpace = $iDotSize - 1  ; ensures that dots have only 1 pixel of space between
            $iDotFrame = -1             ; controls the distance from frame
            Local $a[6][2] = [[3,9], [3,6], [3,3], [6,6], [6,3], [9,3]]
        Case 1.25   ; 125% scaling
            $iDotSize = 3
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case 1.5    ; 150% scaling
            $iDotSize = 3
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case 1.75   ; 175% scaling
            $iDotSize = 3
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case 2      ; 200% scaling
            $iDotSize = 4
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case 2.5    ; 250% scaling
            $iDotSize = 5
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case 3      ; 300% scaling
            $iDotSize = 6
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case 4      ; 400% scaling
            $iDotSize = 8
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
        Case Else
            $iDotSize = 1
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
            Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
    EndSwitch
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    Local $hBrush = _GDIPlus_BrushCreateSolid($iDotsColor)
    _GDIPlus_GraphicsClear($hGraphics, $iBackgroundColor)
    For $i = 0 To UBound($a) - 1
        _GDIPlus_GraphicsFillRect($hGraphics, $iWidth - ($iDotSpace * $a[$i][0]) + $iDotFrame, $iHeight - ($iDotSpace * $a[$i][1]) + $iDotFrame, $iDotSize, $iDotSize, $hBrush)
    Next
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hGraphics)
    Return $hBitmap
EndFunc   ;==>CreateDots

;==============================================
Func _MyGUICtrlStatusBar_SetParts($hWnd, $aPartEdge) ; Pixelsearch

    If Not IsArray($aPartEdge) Then Return False
    Local $iParts = UBound($aPartEdge)
    Local $tParts = DllStructCreate("int[" & $iParts & "]")
    For $i = 0 To $iParts - 1
        DllStructSetData($tParts, 1, $aPartEdge[$i], $i + 1)
    Next
    DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $hWnd, "uint", $SB_SETPARTS, "wparam", $iParts, "struct*", $tParts)
    _GUICtrlStatusBar_Resize($hWnd)
    Return True
EndFunc   ;==>_MyGUICtrlStatusBar_SetParts

;==============================================
Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam) ; Pixelsearch
    #forceref $iMsg, $wParam, $lParam

    If $hWnd = $g_hGUI Then
        Local Static $bIsSizeBoxShown = True
        Local $aSize = WinGetClientSize($g_hGui)
        Local $aGetParts = _GUICtrlStatusBar_GetParts($g_hStatus)
        Local $aParts[$aGetParts[0]]
        For $i = 0 To $aGetParts[0] - 1
            $aParts[$i] = Int($aSize[0] * $g_aRatioW[$i])
        Next
        If BitAND(WinGetState($g_hGui), $WIN_STATE_MAXIMIZED) Then
            _GUICtrlStatusBar_SetParts($g_hStatus, $aParts) ; set parts until GUI right border (AutoIt function forces it)
            _WinAPI_ShowWindow($g_hSizebox, @SW_HIDE)
            $bIsSizeBoxShown = False
        Else
            If $g_aRatioW[UBound($aParts) -1] <> 1 Then $aParts[UBound($aParts) -1] = $aSize[0] - $g_iHeight ; right size of right part stuck on sizebox (no gap)
            _MyGUICtrlStatusBar_SetParts($g_hStatus, $aParts) ; set parts until sizebox (see preceding line) or until GUI right border
            WinMove($g_hSizebox, "", $aSize[0] - $g_iHeight, $aSize[1] - $g_iHeight, $g_iHeight, $g_iHeight)
            If Not $bIsSizeBoxShown Then
                _WinAPI_ShowWindow($g_hSizebox, @SW_SHOW)
                $bIsSizeBoxShown = True
            EndIf
        EndIf
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_SIZE

;==============================================
Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam

    If $hWnd = $g_hGUI Then
        _WinAPI_RedrawWindow($g_hSizebox)
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE

;==============================================
Func WM_DRAWITEM($hWnd, $iMsg, $wParam, $lParam) ; Kafu
    #forceref $hWnd, $iMsg, $wParam

    Local Static $tagDRAWITEM = "uint CtlType;uint CtlID;uint itemID;uint itemAction;uint itemState;hwnd hwndItem;handle hDC;long rcItem[4];ulong_ptr itemData"
    Local $tDRAWITEM = DllStructCreate($tagDRAWITEM, $lParam)
    If $tDRAWITEM.hwndItem <> $g_hStatus Then Return $GUI_RUNDEFMSG ; only process the statusbar

    Local $itemID = $tDRAWITEM.itemID ; status bar part number : 0 if simple bar (or 0, 1, 2... if multi parts)
    Local $hDC = $tDRAWITEM.hDC
    Local $tRect = DllStructCreate("long left;long top;long right;long bottom", DllStructGetPtr($tDRAWITEM, "rcItem"))
    ;_WinAPI_FillRect($hDC, DllStructGetPtr($tRect), $g_hBrush) ; backgound color
    _WinAPI_SetTextColor($hDC, $g_iTextColor) ; text color
    _WinAPI_SetBkMode($hDC, $TRANSPARENT)
    DllStructSetData($tRect, "top", $tRect.top + 1)
    DllStructSetData($tRect, "left", $tRect.left + 1)
    _WinAPI_DrawText($hDC, $g_aText[$itemID], $tRect, $DT_LEFT)

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_DRAWITEM

Func _WinAPI_SetThreadDpiAwarenessContext($DPI_AWARENESS_CONTEXT_value) ; UEZ
    Local $aResult = DllCall("user32.dll", "uint", "SetThreadDpiAwarenessContext", @AutoItX64 ? "int64" : "int", $DPI_AWARENESS_CONTEXT_value) ;requires Win10 v1703+ / Windows Server 2016+
    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_SetThreadDpiAwarenessContext

Func _WinAPI_GetDpiForSystem() ; UEZ
    Local $aResult = DllCall("user32.dll", "uint", "GetDpiForSystem") ;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_GetDpiForSystem

Func _ShowHideSizebox()
    If _WinAPI_IsWindowVisible ($g_hSizebox) Then
        _WinAPI_ShowWindow($g_hSizebox, @SW_HIDE)
        GUICtrlSetData($idChangeText, "Show Fake Sizebox")
    ElseIf Not _WinAPI_IsWindowVisible ($g_hSizebox) Then
        _WinAPI_ShowWindow($g_hSizebox, @SW_SHOW)
        GUICtrlSetData($idChangeText, "Hide Fake Sizebox")
    EndIf
EndFunc

 

Edited by WildByDesign
Posted (edited)
Case 2 To 2.49
...
Case 2.5 To 2.99
...
Case 3 To ...
2 hours ago, argumentum said:

4k at 225%

That looks pretty good. Thanks.

I didn’t even realize that there was a 225%.  The msstyles file doesn’t have resources for 175% or 225% and likely a few others but probably stretches the closest image resources. I’ll probably need to do something like:

EDIT: Ahh. Silly phone always puts the code box at the top. 🙃

Edited by WildByDesign
Posted

Alright, here we go. The following example has the following features:

  • Dark mode statusbar with modern theme applied
  • Updated CreateDots function with full DPI support
    • Dot size, spacing and alignment are identical to gripper dots from light mode Explorer theme
    • Gripper dots tested up to 400% DPI scaling

Somehow it took me 3 days to complete this. But I am finally satisfied now. 😎

#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <GuiStatusBar.au3>
#include <StaticConstants.au3>
#include <WinAPIGdi.au3>
#include <WinAPIRes.au3>
#include <WinAPISysWin.au3>
#include <WinAPITheme.au3>
#include <WindowsConstants.au3>

Opt("MustDeclareVars", 1)

Global $g_hGui, $g_hSizebox, $g_hOldProc, $g_hStatus, $g_iHeight, $g_aText, $g_aRatioW, $g_iBkColor, $g_iTextColor, $g_hDots
Global $g_hBrush

; set base DPI scale value and apply DPI
Global $DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2
Global $iDPI = 1
ApplyDPI()

Func ApplyDPI()
    ; apply System DPI awareness and calculate factor
    _WinAPI_SetThreadDpiAwarenessContext($DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)
    If Not @error Then
        $iDPI = Round(_WinAPI_GetDpiForSystem() / 96, 2)
        If @error Then $iDPI = 1
    Else
        $iDPI = 1
    EndIf
EndFunc

Example()

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

    _GDIPlus_Startup()

    Local Const $SBS_SIZEBOX = 0x08, $SBS_SIZEGRIP = 0x10
    Local $iW = 320 * $iDPI, $iH = 100 * $iDPI
    Dim $g_iBkColor = 0x202020, $g_iTextColor = 0xFFFFFF

    $g_hGui = GUICreate("CreateDots DPI", $iW, $iH, -1, -1, $WS_OVERLAPPEDWINDOW)
    GUISetBkColor(0x181818)

    ;-----------------
    ; Create a sizebox window (Scrollbar class) BEFORE creating the StatusBar control
    $g_hSizebox = _WinAPI_CreateWindowEx(0, "Scrollbar", "", $WS_CHILD + $WS_VISIBLE + $SBS_SIZEBOX, _
        0, 0, 0, 0, $g_hGui) ; $SBS_SIZEBOX or $SBS_SIZEGRIP

    ; Subclass the sizebox (by changing the window procedure associated with the Scrollbar class)
    Local $hProc = DllCallbackRegister('ScrollbarProc', 'lresult', 'hwnd;uint;wparam;lparam')
    $g_hOldProc = _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, DllCallbackGetPtr($hProc))

    Local $hCursor = _WinAPI_LoadCursor(0, $OCR_SIZENWSE)
    _WinAPI_SetClassLongEx($g_hSizebox, -12, $hCursor) ; $GCL_HCURSOR = -12

    $g_hBrush = _WinAPI_CreateSolidBrush($g_iBkColor)

    ;-----------------
    $g_hStatus = _GUICtrlStatusBar_Create($g_hGui, -1, "", $WS_CLIPSIBLINGS) ; ClipSiblings style +++
    Local $aParts[3] = [90, 180, -1]
    If $aParts[Ubound($aParts) - 1] = -1 Then $aParts[Ubound($aParts) - 1] = $iW ; client width size
    _MyGUICtrlStatusBar_SetParts($g_hStatus, $aParts)

    Dim $g_aText[Ubound($aParts)] = ["Part 0", "Part 1", "Part 2"]
    Dim $g_aRatioW[Ubound($aParts)]
    For $i = 0 To UBound($g_aText) - 1
        _GUICtrlStatusBar_SetText($g_hStatus, "", $i, $SBT_OWNERDRAW + $SBT_NOBORDERS)
        $g_aRatioW[$i] = $aParts[$i] / $iW
    Next

    $g_iHeight = _GUICtrlStatusBar_GetHeight($g_hStatus) + 3 ; change the constant (+3) if necessary
    $g_hDots = CreateDots($g_iHeight, $g_iHeight, 0xFF000000 + $g_iBkColor, 0xFF000000 + $g_iTextColor)

    GUIRegisterMsg($WM_SIZE, "WM_SIZE")
    GUIRegisterMsg($WM_MOVE, "WM_MOVE")
    GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM")

    ; apply dark mode to titlebar and statusbar
    _WinAPI_SetWindowTheme($g_hStatus, "DarkMode", "ExplorerStatusBar")
    _WinAPI_DwmSetWindowAttribute($g_hGui, $DWMWA_USE_IMMERSIVE_DARK_MODE, True)
    GUISetState()

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

        EndSwitch
    WEnd

    _GDIPlus_BitmapDispose($g_hDots)
    _GUICtrlStatusBar_Destroy($g_hStatus)
    _WinAPI_DestroyCursor($hCursor)
    _WinAPI_DeleteObject($g_hBrush)
    _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, $g_hOldProc)
    DllCallbackFree($hProc)
    _GDIPlus_Shutdown()
EndFunc   ;==>Example

;==============================================
Func ScrollbarProc($hWnd, $iMsg, $wParam, $lParam) ; Andreik

    If $iMsg = $WM_PAINT Then
        Local $tPAINTSTRUCT
        Local $hDC = _WinAPI_BeginPaint($hWnd, $tPAINTSTRUCT)
        Local $iWidth = DllStructGetData($tPAINTSTRUCT, 'rPaint', 3) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 1)
        Local $iHeight = DllStructGetData($tPAINTSTRUCT, 'rPaint', 4) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 2)
        Local $hGraphics = _GDIPlus_GraphicsCreateFromHDC($hDC)
        _GDIPlus_GraphicsDrawImageRect($hGraphics, $g_hDots, 0, 0, $iWidth, $iHeight)
        _GDIPlus_GraphicsDispose($hGraphics)
        _WinAPI_EndPaint($hWnd, $tPAINTSTRUCT)
        Return 0
    EndIf
    Return _WinAPI_CallWindowProc($g_hOldProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>ScrollbarProc

;==============================================
Func CreateDots($iWidth, $iHeight, $iBackgroundColor, $iDotsColor) ; Andreik
    Local $iDotSize, $iDotSpace, $iDotFrame
    Local $iDPIpct = $iDPI * 100
    Switch $iDPIpct
        ;       Dot Size    Spacing     From Right  From Bottom Confirmed
        ; 100%      2           1           2           2           yes
        ; 125%      3           1           2           2           yes
        ; 150%      3           1           2           2           yes
        ; 175%      3           1           2           2           yes
        ; 200%      4           2           4           4           yes
        ; 250%      5           2           5           5           yes
        ; 300%      6           3           6           6           yes
        ; 400%      8           4           8           8           yes
        Case 100
            $iDotSize = 2
            $iDotSpace = $iDotSize - 0.5    ; gives some control over the spacing between dots
            $iDotFrame = 0.5                ; gives some control over the spacing from frame
        Case 125 To 175
            $iDotSize = 3
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
        Case 200 To 225
            $iDotSize = 4
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
        Case 250 To 275
            $iDotSize = 5
            $iDotSpace = $iDotSize - 1.5
            $iDotFrame = 0
        Case 300 To 375
            $iDotSize = 6
            $iDotSpace = $iDotSize - 1.5
            $iDotFrame = 1
        Case 400 To 500
            $iDotSize = 8
            $iDotSpace = $iDotSize - 2
            $iDotFrame = 2
        Case Else
            $iDotSize = 3
            $iDotSpace = $iDotSize - 1
            $iDotFrame = 1
    EndSwitch

    Local $a[6][2] = [[3,7], [3,5], [3,3], [5,5], [5,3], [7,3]]
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    Local $hBrush = _GDIPlus_BrushCreateSolid($iDotsColor)
    _GDIPlus_GraphicsClear($hGraphics, $iBackgroundColor)
    For $i = 0 To UBound($a) - 1
        _GDIPlus_GraphicsFillRect($hGraphics, $iWidth - ($iDotSpace * $a[$i][0]) + $iDotFrame, $iHeight - ($iDotSpace * $a[$i][1]) + $iDotFrame, $iDotSize, $iDotSize, $hBrush)
    Next
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hGraphics)
    Return $hBitmap
EndFunc   ;==>CreateDots

;==============================================
Func _MyGUICtrlStatusBar_SetParts($hWnd, $aPartEdge) ; Pixelsearch

    If Not IsArray($aPartEdge) Then Return False
    Local $iParts = UBound($aPartEdge)
    Local $tParts = DllStructCreate("int[" & $iParts & "]")
    For $i = 0 To $iParts - 1
        DllStructSetData($tParts, 1, $aPartEdge[$i], $i + 1)
    Next
    DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $hWnd, "uint", $SB_SETPARTS, "wparam", $iParts, "struct*", $tParts)
    _GUICtrlStatusBar_Resize($hWnd)
    Return True
EndFunc   ;==>_MyGUICtrlStatusBar_SetParts

;==============================================
Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam) ; Pixelsearch
    #forceref $iMsg, $wParam, $lParam

    If $hWnd = $g_hGUI Then
        Local Static $bIsSizeBoxShown = True
        Local $aSize = WinGetClientSize($g_hGui)
        Local $aGetParts = _GUICtrlStatusBar_GetParts($g_hStatus)
        Local $aParts[$aGetParts[0]]
        For $i = 0 To $aGetParts[0] - 1
            $aParts[$i] = Int($aSize[0] * $g_aRatioW[$i])
        Next
        If BitAND(WinGetState($g_hGui), $WIN_STATE_MAXIMIZED) Then
            _GUICtrlStatusBar_SetParts($g_hStatus, $aParts) ; set parts until GUI right border (AutoIt function forces it)
            _WinAPI_ShowWindow($g_hSizebox, @SW_HIDE)
            $bIsSizeBoxShown = False
        Else
            If $g_aRatioW[UBound($aParts) -1] <> 1 Then $aParts[UBound($aParts) -1] = $aSize[0] - $g_iHeight ; right size of right part stuck on sizebox (no gap)
            _MyGUICtrlStatusBar_SetParts($g_hStatus, $aParts) ; set parts until sizebox (see preceding line) or until GUI right border
            WinMove($g_hSizebox, "", $aSize[0] - $g_iHeight, $aSize[1] - $g_iHeight, $g_iHeight, $g_iHeight)
            If Not $bIsSizeBoxShown Then
                _WinAPI_ShowWindow($g_hSizebox, @SW_SHOW)
                $bIsSizeBoxShown = True
            EndIf
        EndIf
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_SIZE

;==============================================
Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam

    If $hWnd = $g_hGUI Then
        _WinAPI_RedrawWindow($g_hSizebox)
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE

;==============================================
Func WM_DRAWITEM($hWnd, $iMsg, $wParam, $lParam) ; Kafu
    #forceref $hWnd, $iMsg, $wParam

    Local Static $tagDRAWITEM = "uint CtlType;uint CtlID;uint itemID;uint itemAction;uint itemState;hwnd hwndItem;handle hDC;long rcItem[4];ulong_ptr itemData"
    Local $tDRAWITEM = DllStructCreate($tagDRAWITEM, $lParam)
    If $tDRAWITEM.hwndItem <> $g_hStatus Then Return $GUI_RUNDEFMSG ; only process the statusbar

    Local $itemID = $tDRAWITEM.itemID ; status bar part number : 0 if simple bar (or 0, 1, 2... if multi parts)
    Local $hDC = $tDRAWITEM.hDC
    Local $tRect = DllStructCreate("long left;long top;long right;long bottom", DllStructGetPtr($tDRAWITEM, "rcItem"))
    _WinAPI_FillRect($hDC, DllStructGetPtr($tRect), $g_hBrush) ; backgound color
    _WinAPI_SetTextColor($hDC, $g_iTextColor) ; text color
    _WinAPI_SetBkMode($hDC, $TRANSPARENT)
    DllStructSetData($tRect, "top", $tRect.top + 1)
    DllStructSetData($tRect, "left", $tRect.left + 1)
    _WinAPI_DrawText($hDC, $g_aText[$itemID], $tRect, $DT_LEFT)

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_DRAWITEM

Func _WinAPI_SetThreadDpiAwarenessContext($DPI_AWARENESS_CONTEXT_value) ; UEZ
    Local $aResult = DllCall("user32.dll", "uint", "SetThreadDpiAwarenessContext", @AutoItX64 ? "int64" : "int", $DPI_AWARENESS_CONTEXT_value) ;requires Win10 v1703+ / Windows Server 2016+
    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_SetThreadDpiAwarenessContext

Func _WinAPI_GetDpiForSystem() ; UEZ
    Local $aResult = DllCall("user32.dll", "uint", "GetDpiForSystem") ;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_GetDpiForSystem

 

Posted
8 hours ago, ioa747 said:

and a little further down you will find  "my homework :)

Very nice, thank you for sharing. :)

I wasn't familiar with the _WinAPI_SystemParametersInfo function before but after reading the msdn page I see how powerful it is. Your DPI_Scaling_Utility is definitely a keeper. I like how you provide GUI and CLI options.

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...