Jump to content

Need help with improved dark mode statusbar subclassing


Go to solution Solved by WildByDesign,

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

 

Posted

I have something that works pretty decent so far. If anyone wants to help improve it, please feel free. :)

Dragging the GUI is covered with WM_WINDOWPOSCHANGED and work beautifully.

The most significant issue is that WM_SIZE wont draw it until after the move is complete. So if anyone can help with this part, that would be the issue that needs the most help.

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

#include <WindowsStylesConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiStatusBar.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPITheme.au3>
#include <WinAPIGdiDC.au3>
#include <WinAPIGdi.au3>

Global $g_hStatus, $g_hGui
Global $g_hBrush

Example()

Func Example()
    ; Create GUI
    $g_hGui = GUICreate("StatusBar Resize (v" & @AutoItVersion & ")", 450, 320, 100, 100, $WS_OVERLAPPEDWINDOW)
    GUISetBkColor(0x202020)

    ; Set parts
    Local $hStatus = _GUICtrlStatusBar_Create($g_hGui)
    $g_hStatus = $hStatus

    Local $aParts[3] = [75, 150, -1]
    _GUICtrlStatusBar_SetParts($hStatus, $aParts)
    _GUICtrlStatusBar_SetText($hStatus, "Part 0")
    _GUICtrlStatusBar_SetText($hStatus, "Part 1", 1)
    _GUICtrlStatusBar_SetText($hStatus, "Part 2", 2)

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

    GUISetState(@SW_SHOW)
    _DrawStatusGrip()

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

    ; Loop until the user exits.
    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE
    GUIDelete()
EndFunc   ;==>Example

; Resize the status bar when GUI size changes
Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam
    _GUICtrlStatusBar_Resize($g_hStatus)
    _DrawStatusGrip() ; does not seem to catch it
    AdlibRegister("_DrawStatusGrip", 10) ; seems to be only way so far
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_SIZE

Func _WM_WINDOWPOSCHANGED($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam
    If $hWnd <> $g_hGui Then Return $GUI_RUNDEFMSG
    _DrawStatusGrip()
    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_WINDOWPOSCHANGED

Func _DrawStatusGrip()
    If BitAND(WinGetState($g_hGui), $WIN_STATE_MAXIMIZED) Then Return
    Local Const $SP_GRIPPER = 3
    Local $hDC = _WinAPI_GetDC($g_hGui)
    ; obtain SP_GRIPPER part and size
    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 $aClientSize = WinGetClientSize($g_hGui)

    ; need to fill first to cover borders
    $g_hBrush = _WinAPI_CreateSolidBrush(0x1c1c1c)
    Local $tRECT1 = _WinAPI_CreateRectEx($aClientSize[0] - $g_hGripSize - 2, $aClientSize[1] - $g_hGripSize - 2, $g_hGripSize + 2, $g_hGripSize + 2)
    _WinAPI_FillRect($hDC, DllStructGetPtr($tRECT1), $g_hBrush)

    ; draw SP_GRIPPER part
    Local $tRECT2 = _WinAPI_CreateRectEx($aClientSize[0] - $g_hGripSize, $aClientSize[1] - $g_hGripSize, $g_hGripSize, $g_hGripSize)
    _WinAPI_DrawThemeBackground($hTheme, 3, 0, $hDC, $tRECT2)
    _WinAPI_CloseThemeData($hTheme)
    _WinAPI_ReleaseDC($g_hGui, $hDC)
    _WinAPI_DeleteObject($g_hBrush)
    AdlibUnRegister("_DrawStatusGrip")
EndFunc

 

Posted

If _DrawStatusGrip() doesn't display anymore the gripper when called from functions WM_SIZE / WM_MOVE / WM_WINDOWPOSCHANGED, why not trying it from function WM_EXITSIZEMOVE ?

It seems to work and the gripper reappears correctly when the GUI size/move has ended. Here are the changes I did in your last script :
* No Adlib
* No _DrawStatusGrip() from Func WM_SIZE
* Func _WM_WINDOWPOSCHANGED becomes Func WM_EXITSIZEMOVE
* GUIRegisterMsg($WM_EXITSIZEMOVE, "WM_EXITSIZEMOVE")

Hope it helps :)

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Posted
19 minutes ago, pixelsearch said:

If _DrawStatusGrip() doesn't display anymore the gripper when called from functions WM_SIZE / WM_MOVE / WM_WINDOWPOSCHANGED, why not trying it from function WM_EXITSIZEMOVE ?

Thanks. This works quite well and it is nice to get rid of the Adlib.

21 minutes ago, pixelsearch said:

* Func _WM_WINDOWPOSCHANGED becomes Func WM_EXITSIZEMOVE

I don't think that I can get rid of WM_WINDOWPOSCHANGED though because WM_EXITSIZEMOVE does not work when restoring the window from a maximized state. So I would probably have to keep both of them.

I wish that I could figure out how to get the grip to draw on the statusbar during sizing as well. But I am assuming that it would require subclassing the wm_paint for the statusbar. That is too far above my knowledge right now.

Posted
Just now, WildByDesign said:

I don't think that I can get rid of WM_WINDOWPOSCHANGED though because WM_EXITSIZEMOVE does not work when restoring the window from a maximized state. So I would probably have to keep both of them.

The never ending problem after minimizing / maximizing / restoring :D
WM_WINDOWPOSCHANGED doesn't solve this on my computer and the gripper doesn't reappear after restoring. Also  WM_WINDOWPOSCHANGED is called dozen of times while resizing, it would be great to avoid it.

What follows seems to do it, restoring after maximiming (or after minimizing) makes the gripper reappear :

While True
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            ExitLoop
        Case $GUI_EVENT_RESTORE
            _DrawStatusGrip()
    EndSwitch
WEnd

 

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Posted
17 minutes ago, pixelsearch said:

What follows seems to do it, restoring after maximiming (or after minimizing) makes the gripper reappear :

This works very well, thanks. This suggestion as well as your suggestion to use WM_EXITSIZEMOVE instead of WM_WINDOWPOSCHANGED have both been great suggestions. Plus getting rid of the adlib, of course.

Posted

This current idea of mine is more flawed than I had expected. Since my goal is to create something that can be added on (UDF) to any existing AutoIt GUI script that contains a statusbar. I just realized that this method does not work at all with WS_EX_COMPOSITED and I use that in a few of my GUI projects.

Posted
12 hours ago, WildByDesign said:

I just realized that this method does not work at all with WS_EX_COMPOSITED

If not mistaken, it doesn't work if you apply that style at GUI creation.
Why not trying to apply the style only during the size/move phase, with the following code ?
It works on my computer and the status bar doesn't flicker anymore during the size/move phase

#include <WinAPISysWin.au3>
...
GUIRegisterMsg($WM_ENTERSIZEMOVE, "WM_ENTERSIZEMOVE")
GUIRegisterMsg($WM_EXITSIZEMOVE, "WM_EXITSIZEMOVE")
GUIRegisterMsg($WM_SIZE, "WM_SIZE")
...
Func WM_ENTERSIZEMOVE($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam
    Switch $hWnd
        Case $g_hGui
            _WinAPI_SetWindowLong($g_hGui, $GWL_EXSTYLE, BitOR(_WinAPI_GetWindowLong($g_hGui, $GWL_EXSTYLE), $WS_EX_COMPOSITED))
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_ENTERSIZEMOVE

Func WM_EXITSIZEMOVE($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam
    Switch $hWnd
        Case $g_hGui
            _WinAPI_SetWindowLong($g_hGui, $GWL_EXSTYLE, BitXOR(_WinAPI_GetWindowLong($g_hGui, $GWL_EXSTYLE), $WS_EX_COMPOSITED))
            _DrawStatusGrip()
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_EXITSIZEMOVE

Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam
    Switch $hWnd
        Case $g_hGui
            _GUICtrlStatusBar_Resize($g_hStatus)
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_SIZE

 

"I think you are searching a bug where there is no bug... don't listen to bad advice."

  • Solution
Posted
3 hours ago, pixelsearch said:

Why not trying to apply the style only during the size/move phase, with the following code ?

This works quite well for the purpose of us testing it. But overall, I'm not sure my current idea is best for the perspective of a UDF. Someone might have a GUI that relies on WS_EX_COMPOSITED, so I don't want to make changes that can cause problems.

With that being said, I think that your original script which uses a Scrollbar window to paint the dots on is likely the best option. It is very versatile and works in way more scenarios. I tried that initially but ran into problems. The problems that I had related to the following comment from your script:

; Create a sizebox window (Scrollbar class) BEFORE creating the StatusBar control

You put that comment there with very good reason. That is what would determine Z-order and is very crucial for how your script works.

From the perspective, the statusbar would already be created. At some point, I tried collecting status details, destroy statusbar and then create sizebox and statusbar. I found a way to fix this. So now, I collect status details, create sizebox and change Z-order with the following:

_WinAPI_SetWindowPos($g_hSizebox, $HWND_TOP, $aPos[0], $aPos[1], $aPos[2], $aPos[3], 0)

This method seems to work very well. I will share the script from where I left off. It isn't complete yet. I have to collect the details of the statusbar and do the math/measurements for the ratio (for resizing parts) after creation because in this case we are (in theory) working on other GUIs. I still have to clean up a bit more before adding to GUIDarkTheme UDF. More testing as well. But so far, so good. :)

I had to change some measurements to not cut off the statusbar borders as well.

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>
#include <Array.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
Global $hTheme = _WinAPI_OpenThemeData($g_hGui, 'Status')
Global $tSIZE = _WinAPI_GetThemePartSize($hTheme, $SP_GRIPPER, 0, Null, Null, $TS_TRUE)
Global $g_hGripSize = $tSIZE.X

Global $hStatusProc = DllCallbackRegister('__GUIDarkMode_StatusProc', 'ptr', 'hwnd;uint;wparam;lparam')
Global $hPrevProc, $hCursor, $hSizeboxProc

OnAutoItExitRegister("_ExitStatus")

Func _ExitStatus()
    _WinAPI_CloseThemeData($hTheme)
    _WinAPI_SetWindowLong($g_hGui, -4, $hPrevProc)
    DllCallbackFree($hStatusProc)
    _GDIPlus_BitmapDispose($g_hDots)
    _GUICtrlStatusBar_Destroy($g_hStatus)
    _WinAPI_DestroyCursor($hCursor)
    _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, $g_hOldProc)
    DllCallbackFree($hSizeboxProc)
    _GDIPlus_Shutdown()
EndFunc

Example()

Func Example()

    _GDIPlus_Startup()

    Local $iW = 300, $iH = 100
    ;Dim $g_iBkColor = 0x1c1c1c, $g_iTextColor = 0xFFFFFF
    Dim $g_iBkColor = 0x1c1c1c, $g_iTextColor = 0xFFFFFF

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

    $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, $g_aText[$i], $i, $SBT_NOBORDERS)
    Next

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

    __GUIDarkMode_StatusRatio()
    __GUIDarkMode_CreateSizebox()

    _WinAPI_SetWindowTheme($g_hStatus, 'DarkMode', 'ExplorerStatusBar') ; 0x1c1c1c background
    ;_WinAPI_SetWindowTheme($g_hStatus, 'DarkMode_DarkTheme', 'Status') ; 0x3b3b3b background (Win11 24H2/25H2 only)
    $hPrevProc = _WinAPI_SetWindowLong($g_hGui, -4, DllCallbackGetPtr($hStatusProc))

    GUISetState()

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                Exit

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

Func __GUIDarkMode_SizeboxProc($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_GraphicsDrawImageRect($hGraphics, $g_hDots, 0, 0, $g_hGripSize + 2, $g_hGripSize + 2)
        _GDIPlus_GraphicsDispose($hGraphics)

        ; paint sizegrip
        ;Local $tRECT = _WinAPI_CreateRectEx($g_iHeight - $g_hGripSize, $g_iHeight - $g_hGripSize, $g_hGripSize, $g_hGripSize)
        Local $tRECT = _WinAPI_CreateRectEx($g_hGripSize + 2 - $g_hGripSize + 1, $g_hGripSize + 2 - $g_hGripSize + 1, $g_hGripSize, $g_hGripSize)
        _WinAPI_DrawThemeBackground($hTheme, 3, 0, $hDC, $tRECT)

        _WinAPI_EndPaint($hWnd, $tPAINTSTRUCT)
        Return 0
    EndIf
    Return _WinAPI_CallWindowProc($g_hOldProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>__GUIDarkMode_SizeboxProc

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
        _WinAPI_RedrawWindow($g_hSizebox)
        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)
            WinMove($g_hSizebox, "", $aSize[0] - $g_hGripSize - 2 - 1, $aSize[1] - $g_hGripSize - 2 - 1, $g_hGripSize + 2, $g_hGripSize + 2)
            If Not $bIsSizeBoxShown Then
                _WinAPI_ShowWindow($g_hSizebox, @SW_SHOW)
                $bIsSizeBoxShown = True
            EndIf
            _WinAPI_RedrawWindow($g_hSizebox)
        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 __GUIDarkMode_StatusProc($hWnd, $iMsg, $wParam, $lParam)
    Local $sContinue = $GUI_RUNDEFMSG
    Switch $iMsg
        Case $WM_SIZE
            $sContinue = _WM_SIZE($hWnd, $iMsg, $wParam, $lParam)
        Case $WM_WINDOWPOSCHANGED
            $sContinue = _WM_WINDOWPOSCHANGED($hWnd, $iMsg, $wParam, $lParam)
    EndSwitch
    If $sContinue = $GUI_RUNDEFMSG Then Return _WinAPI_CallWindowProc($hPrevProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>__GUIDarkMode_StatusProc

Func __GUIDarkMode_StatusRatio()
    ; calculate ratio need for the resizing of statusbar parts
    Local $iGuiWidth = WinGetClientSize($g_hGui)[0]
    Local $aParts = _GUICtrlStatusBar_GetParts($g_hStatus)
    _ArrayDelete($aParts, 0)
    Dim $g_aRatioW[Ubound($aParts)]
    For $i = 0 To UBound($aParts) - 1
        $g_aRatioW[$i] = $aParts[$i] / $iGuiWidth
    Next
EndFunc   ;==>__GUIDarkMode_StatusRatio

Func __GUIDarkMode_CreateSizebox()
    ; create grip
    $g_iHeight = WinGetPos($g_hStatus)[3] - 3
    ;$g_hDots = CreateDots($g_iHeight, $g_iHeight, 0xFF000000 + $g_iBkColor, 0xFF000000 + $g_iTextColor)
    $g_hDots = CreateDots($g_hGripSize + 2, $g_hGripSize + 2, 0xFF000000 + $g_iBkColor, 0xFF000000 + $g_iTextColor)

    Local Const $SBS_SIZEBOX = 0x08, $SBS_SIZEGRIP = 0x10
    ; Create a sizebox window (Scrollbar class)
    $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)
    $hSizeboxProc = DllCallbackRegister('__GUIDarkMode_SizeboxProc', 'lresult', 'hwnd;uint;wparam;lparam')
    $g_hOldProc = _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, DllCallbackGetPtr($hSizeboxProc))

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

    ; Fix Z-order of SizeboxProc (needed for cursor)
    Local $aPos = WinGetPos($g_hSizebox)
    _WinAPI_SetWindowPos($g_hSizebox, $HWND_TOP, $aPos[0], $aPos[1], $aPos[2], $aPos[3], 0)
EndFunc   ;==>__GUIDarkMode_CreateSizebox

 

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