Jump to content

Recommended Posts

Posted

This is essentially a continuation of the Resizable status bar without SBARS_SIZEGRIP topic in which the wonderful @pixelsearch figured out a solution for doing an OWNERDRAW of the dark mode statusbar parts. The sizegrip dots are drawn using GDI+ in that example.

I have since figured out a method using _WinAPI_DrawThemeBackground to paint the SP_GRIPPER part and _WinAPI_GetThemePartSize to ensure proper size with high DPI support.

My overall goal is to add statusbar support to the GUIDarkTheme UDF. The problem that I am facing is that the method used in the example from that thread creates a separate window (Scrollbar class) to paint the dots on. And while that works beautifully in the example, it becomes much more complex adding it to the UDF. The Scrollbar window constantly loses focus and disappears in examples that have more controls such as the SampleControls GUI example.

Since Notepad++ has some of the absolute best dark mode subclassing techniques, I thought to look there. First of all, I noticed that Notepad++ does not use a separate window to paint the SP_GRIPPER part. They seem to paint it directly on the statusbar which would likely solve my issues with the Scrollbar window disappearing.

All of the subclassing code for Notepad++ statusbar is contained in StatusBar.cpp.

One thing that I found interesting is that they use ODA_DRAWENTIRE which we don't seem to have a Const variable for in AutoIt.

They make use of WM_ERASEBKGND, WM_PAINT and WM_PRINTCLIENT. The majority of the drawing seems to take place in WM_PRINTCLIENT. They seem to paint the individual parts the same way as @pixelsearch does in his example.

However, the part that I don't understand at all is how they paint the SP_GRIPPER to the bottom right corner of the statusbar.

I will share an example of where I left off. It has the code for obtaining, measuring and painting the SP_GRIPPER in the ScrollbarProc function because that is the only thing that I could figure out. But ideally, I would like to get rid of the Scrollbar window and paint directly on the statusbar as Notepad++ does.

If there is somebody that has the subclassing understanding and is willing to help with this, I would really appreciate it. As always, I appreciate everyone's time and efforts. Thank you. :)

DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", @AutoItX64 ? "int64" : "int", -2)

#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 Const $SP_GRIPPER = 3

Example()

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

    _GDIPlus_Startup()

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

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

    ;-----------------
    ; 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_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
    _GUICtrlStatusBar_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, $g_aText[$i], $i)
        _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

    $g_iHeight = WinGetPos($g_hStatus)[3] - 3
    $g_hDots = CreateDots($g_iHeight, $g_iHeight, 0xFF000000 + $g_iBkColor, 0xFF000000 + $g_iTextColor)

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

    _WinAPI_SetWindowTheme($g_hStatus, 'DarkMode', 'ExplorerStatusBar') ; 0x1c1c1c background
    ;_WinAPI_SetWindowTheme($g_hStatus, 'DarkMode_DarkTheme', 'Status') ; 0x3b3b3b background (Win11 24H2/25H2 only)

    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
                    ;_GUICtrlStatusBar_SetText($g_hStatus, $g_aText[$i] & " : Inc " & $iInc , $i)
                Next
                _WinAPI_RedrawWindow($g_hStatus)
        EndSwitch
    WEnd

    _GDIPlus_BitmapDispose($g_hDots)
    _GUICtrlStatusBar_Destroy($g_hStatus)
    _WinAPI_DestroyCursor($hCursor)
    _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)

        ; paint SP_GRIPPER
        Local $hTheme = _WinAPI_OpenThemeData($g_hGui, 'Status')
        Local $tSIZE = _WinAPI_GetThemePartSize($hTheme, $SP_GRIPPER, 0, Null, Null, $TS_TRUE)
        Local $g_hGripSize = $tSIZE.X
        Local $tRECT = _WinAPI_CreateRectEx($g_iHeight - $g_hGripSize, $g_iHeight - $g_hGripSize, $g_hGripSize, $g_hGripSize)
        _WinAPI_SetBkMode($hDC, $TRANSPARENT)
        _WinAPI_DrawThemeBackground($hTheme, 3, 0, $hDC, $tRECT)
        _WinAPI_CloseThemeData($hTheme)

        _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 $hBitmap = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    Local $hBrush = _GDIPlus_BrushCreateSolid($iDotsColor)
    _GDIPlus_GraphicsClear($hGraphics, $iBackgroundColor)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hGraphics)
    Return $hBitmap
EndFunc   ;==>CreateDots

;==============================================
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)
            _GUICtrlStatusBar_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_WINDOWPOSCHANGED($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam

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

;==============================================
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

 

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