Jump to content

Need help subclassing checkbox control


Go to solution Solved by WildByDesign,

Recommended Posts

Posted (edited)

I have made some success so far with subclassing a checkbox control but I need some help, please. :)

This is intended for Windows 11 (24H2 and earlier) builds where there is a nice checkbox but the text is black.

Unfortunately I've got to go out for most of the day but I've tried to comment the code as best as I can and there is some good success going on. If anyone can help improve the positioning and alignment and things like that to extend or improve, please feel free to help. Thank you so much. :)

And by the way, if this becomes successful (when it becomes successful), we should very easily be able to extend this to radio controls as well.

#include <WinAPITheme.au3>
#include <WinAPIGdi.au3>
#include <APIGdiConstants.au3>
#include <StructureConstants.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPISysInternals.au3>
#include <WinAPIShellEx.au3>
#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>

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

Example()

Func Example()
    ; Create a GUI with various controls.
    Local $hGUI = GUICreate("Example", 300, 200)
    GUISetBkColor("0x202020")

    ; Create a checkbox control.
    Local $idCheckbox = GUICtrlCreateCheckbox("Standard Checkbox", 69, 60, 185, 25)
    Local $idButton_Close = GUICtrlCreateButton("Close", 210, 170, 85, 25)

    Local $hCheckbox = GUICtrlGetHandle($idCheckbox)

    ; Register Subclassing / Window Procedure
    Local $pAddress = _WinAPI_GetWindowLong($hCheckbox, $GWL_WNDPROC)
    Local $hSubclass = DllCallbackRegister(_CheckboxProc, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    _WinAPI_SetWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox, $pAddress)

    ; Display the GUI.
    GUISetState(@SW_SHOW, $hGUI)

    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idButton_Close
                ExitLoop

            Case $idCheckbox
                If _IsChecked($idCheckbox) Then
                    MsgBox($MB_SYSTEMMODAL, "", "The checkbox is checked.", 0, $hGUI)
                Else
                    MsgBox($MB_SYSTEMMODAL, "", "The checkbox is not checked.", 0, $hGUI)
                EndIf

        EndSwitch
    WEnd

    ; Cleanup: Restore original Window Procedure
    _WinAPI_RemoveWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox)
    DllCallbackFree($hSubclass)

    ; Delete the previous GUI and all controls.
    GUIDelete($hGUI)
EndFunc   ;==>Example

Func _IsChecked($idControlID)
    Return BitAND(GUICtrlRead($idControlID), $GUI_CHECKED) = $GUI_CHECKED
EndFunc   ;==>_IsChecked

Func _CheckboxProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Local Static $bHover, $tPoint
    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1 ; Prevent background erasing to avoid flickering

        ;Case $WM_PRINTCLIENT

        Case $WM_PAINT
            Local Const $BP_CHECKBOX = 3

            ; checkbox StateID
            Local Const $CBS_UNCHECKEDNORMAL = 1
            Local Const $CBS_UNCHECKEDHOT = 2
            Local Const $CBS_UNCHECKEDPRESSED = 3
            Local Const $CBS_UNCHECKEDDISABLED = 4
            Local Const $CBS_CHECKEDNORMAL = 5
            Local Const $CBS_CHECKEDHOT = 6
            Local Const $CBS_CHECKEDPRESSED = 7
            Local Const $CBS_CHECKEDDISABLED = 8
            Local Const $CBS_MIXEDNORMAL = 9
            Local Const $CBS_MIXEDHOT = 10
            Local Const $CBS_MIXEDPRESSED = 11
            Local Const $CBS_MIXEDDISABLED = 12

            Local $hTheme = _WinAPI_OpenThemeData($hWnd, "DarkMode_Explorer::Button")

            Local $tPaint = DllStructCreate($tagPAINTSTRUCT)
            Local $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)

            ; need to get PartID and StateID
            ; https://learn.microsoft.com/en-us/windows/win32/controls/parts-and-states

            ; GetThemeBackgroundContentRect
            ; _WinAPI_GetThemeBackgroundContentRect (not done yet)
            Local $tSIZE = _WinAPI_GetThemePartSize($hTheme, $BP_CHECKBOX, 0, Null, Null, $TS_TRUE)
            ConsoleWrite("checkbox size: " & $tSIZE.X & @CRLF)

            ; DrawThemeParentBackground
            ; _WinAPI_DrawThemeParentBackground (not done yet)

            ; DrawThemeBackground
            Local $tRect = _WinAPI_CreateRectEx(0, 0, $tSIZE.X, $tSIZE.Y)
            _WinAPI_DrawThemeBackground($hTheme, $BP_CHECKBOX, $CBS_CHECKEDNORMAL, $hDC, $tRect)

            Local $sText = _WinAPI_GetWindowText($hWnd)
            ConsoleWrite("button text: " & $sText & @CRLF)

            ; Setup font
            Local $hFont = _SendMessage($hWnd, $WM_GETFONT, 0, 0)
            If Not $hFont Then $hFont = _WinAPI_GetStockObject($DEFAULT_GUI_FONT)
            Local $hOldFont = _WinAPI_SelectObject($hDC, $hFont)

            _WinAPI_SetBkMode($hDC, $TRANSPARENT)
            _WinAPI_SetTextColor($hDC, _WinAPI_SwitchColor(0xFFFFFF))

            Local $tTextRect = DllStructCreate($tagRECT)

            DllCall("user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sText, "int", -1, "struct*", $tTextRect, "uint", BitOR($DT_CENTER, $DT_VCENTER, $DT_SINGLELINE, $DT_NOPREFIX, $DT_NOCLIP))

            ; cleanup
            _WinAPI_SelectObject($hDC, $hOldFont)
            _WinAPI_CloseThemeData($hTheme)

            _WinAPI_EndPaint($hWnd, $tPaint)
            Return 0

    EndSwitch

    Return __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_CheckboxProc

Func __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
    Return DllCall('comctl32.dll', 'lresult', 'DefSubclassProc', 'hwnd', $hWnd, 'uint', $iMsg, 'wparam', $wParam, _
            'lparam', $lParam)[0]
EndFunc   ;==>__WinAPI_DefSubclassProc

 

Edited by WildByDesign
updated example
Posted

Not at my PC right now, but I was thinking that we should probably have the theme handle as Static because otherwise it is opening and closing the theme file hundreds of times.

We should be able to close the theme in a WM_DESTROY message.

The theme part measurements likely should be Static as well except for text.

Posted

@argumentum I need your help if you have a moment. Particularly, if you can test the following script on 24H2.

Please keep in mind that the code is a messy while I'm figuring it out. The text alignment is way off. But the main question is, does it work? Do you get white text on 24H2?

If yes, then I can start to clean it up. I got the checkbox states working as far as I can tell.

#include <GuiButton.au3>
#include <APIThemeConstants.au3>
#include <WinAPITheme.au3>
#include <WinAPIGdi.au3>
#include <APIGdiConstants.au3>
#include <StructureConstants.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPISysInternals.au3>
#include <WinAPIShellEx.au3>
#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>

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

Example()

Func Example()
    ; Create a GUI with various controls.
    Local $hGUI = GUICreate("Example", 300, 200)
    GUISetBkColor("0x202020")

    ; Create a checkbox control.
    Local $idCheckbox = GUICtrlCreateCheckbox("Standard Checkbox", 69, 60, 185, 25)
    Local $idButton_Close = GUICtrlCreateButton("Close", 210, 170, 85, 25)

    Local $hCheckbox = GUICtrlGetHandle($idCheckbox)

    ; Register Subclassing / Window Procedure
    Local $pAddress = _WinAPI_GetWindowLong($hCheckbox, $GWL_WNDPROC)
    Local $hSubclass = DllCallbackRegister(_CheckboxProc, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    _WinAPI_SetWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox, $pAddress)

    ; Display the GUI.
    GUISetState(@SW_SHOW, $hGUI)

    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idButton_Close
                ExitLoop

            Case $idCheckbox
                If _IsChecked($idCheckbox) Then
                    ;MsgBox($MB_SYSTEMMODAL, "", "The checkbox is checked.", 0, $hGUI)
                    ConsoleWrite("The checkbox is checked." & @CRLF)
                Else
                    ;MsgBox($MB_SYSTEMMODAL, "", "The checkbox is not checked.", 0, $hGUI)
                    ConsoleWrite("The checkbox is not checked." & @CRLF)
                EndIf

        EndSwitch
    WEnd

    ; Cleanup: Restore original Window Procedure
    _WinAPI_RemoveWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox)
    DllCallbackFree($hSubclass)

    ; Delete the previous GUI and all controls.
    GUIDelete($hGUI)
EndFunc   ;==>Example

Func _IsChecked($idControlID)
    Return BitAND(GUICtrlRead($idControlID), $GUI_CHECKED) = $GUI_CHECKED
EndFunc   ;==>_IsChecked

Func _CheckboxProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Local Static $bHover, $tPoint
    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1 ; Prevent background erasing to avoid flickering

        ;Case $WM_PRINTCLIENT

        Case $WM_PAINT
            Local $iStateID = 0
            Local Const $BP_CHECKBOX = 3

            Local Const $BST_HOT = 0x0200

            ; checkbox StateID
            Local Const $CBS_UNCHECKEDNORMAL = 1
            Local Const $CBS_UNCHECKEDHOT = 2
            Local Const $CBS_UNCHECKEDPRESSED = 3
            Local Const $CBS_UNCHECKEDDISABLED = 4
            Local Const $CBS_CHECKEDNORMAL = 5
            Local Const $CBS_CHECKEDHOT = 6
            Local Const $CBS_CHECKEDPRESSED = 7
            Local Const $CBS_CHECKEDDISABLED = 8
            Local Const $CBS_MIXEDNORMAL = 9
            Local Const $CBS_MIXEDHOT = 10
            Local Const $CBS_MIXEDPRESSED = 11
            Local Const $CBS_MIXEDDISABLED = 12

            Local $hTheme = _WinAPI_OpenThemeData($hWnd, "DarkMode_Explorer::Button")

            Local $tPaint = DllStructCreate($tagPAINTSTRUCT)
            Local $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)

            ; need to get PartID and StateID
            ; https://learn.microsoft.com/en-us/windows/win32/controls/parts-and-states

            ; GetThemeBackgroundContentRect
            ; _WinAPI_GetThemeBackgroundContentRect (not done yet)
            Local $tSIZE = _WinAPI_GetThemePartSize($hTheme, $BP_CHECKBOX, 0, Null, Null, $TS_TRUE)

            Local $tRectClient = _WinAPI_GetClientRect($hWnd)

            Local $sText = _WinAPI_GetWindowText($hWnd)

            Local $iState = _GUICtrlButton_GetState($hWnd)
            ;ConsoleWrite("checkbox state: " & $iState & @CRLF)
            ;ConsoleWrite("$BST_FOCUS: " & $BST_FOCUS & @CRLF)
            #cs
            If BitAND($iState, $BST_HOT) Then ConsoleWrite("BST_HOT" & @CRLF)
            If BitAND($iState, $BST_INDETERMINATE) Then ConsoleWrite("BST_INDETERMINATE" & @CRLF)
            If BitAND($iState, $BST_UNCHECKED) Then ConsoleWrite("BST_UNCHECKED" & @CRLF)
            If BitAND($iState, $BST_PUSHED) Then
                ConsoleWrite("BST_PUSHED" & @CRLF)
                $iStateID = $CBS_UNCHECKEDPRESSED
            EndIf
            If BitAND($iState, $BST_DONTCLICK) Then ConsoleWrite("BST_DONTCLICK" & @CRLF)
            #ce

            If Not _WinAPI_IsWindowEnabled($hWnd) Then
                $iStateID = $CBS_UNCHECKEDDISABLED
            ElseIf BitAND($iState, $BST_PUSHED) Then
                ConsoleWrite("BST_PUSHED" & @CRLF)
                $iStateID = $CBS_UNCHECKEDPRESSED
            ElseIf BitAND($iState, $BST_HOT) Then
                ConsoleWrite("BST_HOT" & @CRLF)
                $iStateID = $CBS_UNCHECKEDHOT
            Else
                ConsoleWrite("CBS_UNCHECKEDNORMAL" & @CRLF)
                $iStateID = $CBS_UNCHECKEDNORMAL
            EndIf

            If BitAND($iState, $BST_CHECKED) Then
                ConsoleWrite("BST_CHECKED" & @CRLF)
                $iStateID = $CBS_CHECKEDNORMAL
            ElseIf BitAND($iState, $BST_INDETERMINATE) Then
                ConsoleWrite("BST_INDETERMINATE" & @CRLF)
                $iStateID = $CBS_MIXEDNORMAL
            EndIf

            ; DrawThemeBackground
            Local $tRect = _WinAPI_CreateRectEx(0, 0, $tSIZE.X, $tSIZE.Y)
            _WinAPI_DrawThemeParentBackground($hWnd, $hDC, $tRectClient)
            _WinAPI_DrawThemeBackground($hTheme, $BP_CHECKBOX, $iStateID, $hDC, $tRect)

            ; Setup theme font
            ;Local $tLOGFONT = _WinAPI_GetThemeFont($hTheme, $BP_CHECKBOX, $CBS_CHECKEDNORMAL, $TMT_FONT)
            ;Local $hFont = _WinAPI_CreateFontIndirect($tLOGFONT)
            ;Local $hOldFont = _WinAPI_SelectObject($hDC, $hFont)

            Local $tDTTOPTS = DllStructCreate($tagDTTOPTS)
            DllStructSetData($tDTTOPTS, 'Size', DllStructGetSize($tDTTOPTS))
            DllStructSetData($tDTTOPTS, 'Flags', BitOR($DTT_TEXTCOLOR, $DTT_COMPOSITED))
            DllStructSetData($tDTTOPTS, 'clrText', 0xFFFFFF)

            ; Setup font
            Local $hFont = _SendMessage($hWnd, $WM_GETFONT, 0, 0)
            If Not $hFont Then $hFont = _WinAPI_GetStockObject($DEFAULT_GUI_FONT)
            Local $hOldFont = _WinAPI_SelectObject($hDC, $hFont)

            ;_WinAPI_SetBkMode($hDC, $TRANSPARENT)
            ;_WinAPI_SetTextColor($hDC, _WinAPI_SwitchColor(0xFFFFFF))

            ;Local $tTextRect = DllStructCreate($tagRECT)

            _WinAPI_DrawThemeTextEx($hTheme, $BP_CHECKBOX, $CBS_CHECKEDNORMAL, $hDC, $sText, $tRectClient, BitOR($DT_CENTER, $DT_SINGLELINE, $DT_VCENTER), $tDTTOPTS)

            ;DllCall("user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sText, "int", -1, "struct*", $tTextRect, "uint", BitOR($DT_CENTER, $DT_VCENTER, $DT_SINGLELINE, $DT_NOPREFIX, $DT_NOCLIP))

            ; cleanup
            _WinAPI_SelectObject($hDC, $hOldFont)
            _WinAPI_CloseThemeData($hTheme)

            _WinAPI_EndPaint($hWnd, $tPaint)
            Return 0

    EndSwitch

    Return __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_CheckboxProc

Func __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
    Return DllCall('comctl32.dll', 'lresult', 'DefSubclassProc', 'hwnd', $hWnd, 'uint', $iMsg, 'wparam', $wParam, _
            'lparam', $lParam)[0]
EndFunc   ;==>__WinAPI_DefSubclassProc

 

Posted
5 minutes ago, argumentum said:

..not the same PC but same OS version ( in the other am running AutoIt 3.3.16.1 )

Thanks for confirming. I appreciate it. So that is really good that it's working. Now I have to figure out the text rectangle / alignment math to ensure that everything is aligned better. That is not my best skill but I will figure it out. :)

 

Posted
54 minutes ago, argumentum said:

..not the same PC but same OS version ( in the other am running AutoIt 3.3.16.1 )

Can you please test the checkbox and text alignment with this one? :)

It looks correct on mine with or without DPI. But every system is different, so I am crossing my fingers on this one.

#include <GuiButton.au3>
#include <APIThemeConstants.au3>
#include <WinAPITheme.au3>
#include <WinAPIGdi.au3>
#include <APIGdiConstants.au3>
#include <StructureConstants.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPISysInternals.au3>
#include <WinAPIShellEx.au3>
#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>

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

Example()

Func Example()
    ; Create a GUI with various controls.
    Local $hGUI = GUICreate("Example", 300, 200)
    GUISetBkColor("0x202020")

    ; Create a checkbox control.
    Local $idCheckbox = GUICtrlCreateCheckbox("Standard Checkbox", 69, 60, 185, 25)
    Local $idButton_Close = GUICtrlCreateButton("Close", 210, 170, 85, 25)

    Local $hCheckbox = GUICtrlGetHandle($idCheckbox)

    ; Register Subclassing / Window Procedure
    Local $pAddress = _WinAPI_GetWindowLong($hCheckbox, $GWL_WNDPROC)
    Local $hSubclass = DllCallbackRegister(_CheckboxProc, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    _WinAPI_SetWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox, $pAddress)

    ; Display the GUI.
    GUISetState(@SW_SHOW, $hGUI)

    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idButton_Close
                ExitLoop

            Case $idCheckbox
                If _IsChecked($idCheckbox) Then
                    ;MsgBox($MB_SYSTEMMODAL, "", "The checkbox is checked.", 0, $hGUI)
                    ConsoleWrite("The checkbox is checked." & @CRLF)
                Else
                    ;MsgBox($MB_SYSTEMMODAL, "", "The checkbox is not checked.", 0, $hGUI)
                    ConsoleWrite("The checkbox is not checked." & @CRLF)
                EndIf

        EndSwitch
    WEnd

    ; Cleanup: Restore original Window Procedure
    _WinAPI_RemoveWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox)
    DllCallbackFree($hSubclass)

    ; Delete the previous GUI and all controls.
    GUIDelete($hGUI)
EndFunc   ;==>Example

Func _IsChecked($idControlID)
    Return BitAND(GUICtrlRead($idControlID), $GUI_CHECKED) = $GUI_CHECKED
EndFunc   ;==>_IsChecked

Func _CheckboxProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Local Static $bHover, $tPoint
    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1 ; Prevent background erasing to avoid flickering

        ;Case $WM_PRINTCLIENT

        Case $WM_PAINT
            Local $iStateID = 0
            Local Const $BP_CHECKBOX = 3

            Local Const $BST_HOT = 0x0200

            ; checkbox StateID
            Local Const $CBS_UNCHECKEDNORMAL = 1
            Local Const $CBS_UNCHECKEDHOT = 2
            Local Const $CBS_UNCHECKEDPRESSED = 3
            Local Const $CBS_UNCHECKEDDISABLED = 4
            Local Const $CBS_CHECKEDNORMAL = 5
            Local Const $CBS_CHECKEDHOT = 6
            Local Const $CBS_CHECKEDPRESSED = 7
            Local Const $CBS_CHECKEDDISABLED = 8
            Local Const $CBS_MIXEDNORMAL = 9
            Local Const $CBS_MIXEDHOT = 10
            Local Const $CBS_MIXEDPRESSED = 11
            Local Const $CBS_MIXEDDISABLED = 12

            Local $hTheme = _WinAPI_OpenThemeData($hWnd, "DarkMode_Explorer::Button")

            Local $tPaint = DllStructCreate($tagPAINTSTRUCT)
            Local $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)

            Local $tClient = _WinAPI_GetClientRect($hWnd)
            Local $iW = $tClient.Right
            Local $iH = $tClient.Bottom

            ; Initiate double buffering
            Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
            Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iW, $iH)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

            Local $iState = _GUICtrlButton_GetState($hWnd)
            ;ConsoleWrite("checkbox state: " & $iState & @CRLF)
            ;ConsoleWrite("$BST_FOCUS: " & $BST_FOCUS & @CRLF)
            #cs
            If BitAND($iState, $BST_HOT) Then ConsoleWrite("BST_HOT" & @CRLF)
            If BitAND($iState, $BST_INDETERMINATE) Then ConsoleWrite("BST_INDETERMINATE" & @CRLF)
            If BitAND($iState, $BST_UNCHECKED) Then ConsoleWrite("BST_UNCHECKED" & @CRLF)
            If BitAND($iState, $BST_PUSHED) Then
                ConsoleWrite("BST_PUSHED" & @CRLF)
                $iStateID = $CBS_UNCHECKEDPRESSED
            EndIf
            If BitAND($iState, $BST_DONTCLICK) Then ConsoleWrite("BST_DONTCLICK" & @CRLF)
            #ce

            If Not _WinAPI_IsWindowEnabled($hWnd) Then
                $iStateID = $CBS_UNCHECKEDDISABLED
            ElseIf BitAND($iState, $BST_PUSHED) Then
                ConsoleWrite("BST_PUSHED" & @CRLF)
                $iStateID = $CBS_UNCHECKEDPRESSED
            ElseIf BitAND($iState, $BST_HOT) Then
                ConsoleWrite("BST_HOT" & @CRLF)
                $iStateID = $CBS_UNCHECKEDHOT
            Else
                ConsoleWrite("CBS_UNCHECKEDNORMAL" & @CRLF)
                $iStateID = $CBS_UNCHECKEDNORMAL
            EndIf

            If BitAND($iState, $BST_CHECKED) Then
                ConsoleWrite("BST_CHECKED" & @CRLF)
                $iStateID = $CBS_CHECKEDNORMAL
            ElseIf BitAND($iState, $BST_INDETERMINATE) Then
                ConsoleWrite("BST_INDETERMINATE" & @CRLF)
                $iStateID = $CBS_MIXEDNORMAL
            EndIf

            ; GetThemeBackgroundContentRect
            Local $tTextRect = _WinAPI_GetThemeBackgroundContentRect($hTheme, $BP_CHECKBOX, $iStateID, $hMemDC, $tClient)
            Local $tSIZE = _WinAPI_GetThemePartSize($hTheme, $BP_CHECKBOX, 0, Null, Null, $TS_TRUE)

            ;Local $tRectClient = _WinAPI_GetClientRect($hWnd)

            Local $sText = _WinAPI_GetWindowText($hWnd)

            ; DrawThemeBackground
            Local $tRect = _WinAPI_CreateRectEx(0, 0, $tSIZE.X, $tSIZE.Y)
            _WinAPI_DrawThemeParentBackground($hWnd, $hMemDC, $tClient)
            _WinAPI_DrawThemeBackground($hTheme, $BP_CHECKBOX, $iStateID, $hMemDC, $tRect)

            ; Setup theme font
            ;Local $tLOGFONT = _WinAPI_GetThemeFont($hTheme, $BP_CHECKBOX, $CBS_CHECKEDNORMAL, $TMT_FONT)
            ;Local $hFont = _WinAPI_CreateFontIndirect($tLOGFONT)
            ;Local $hOldFont = _WinAPI_SelectObject($hDC, $hFont)

            Local $tDTTOPTS = DllStructCreate($tagDTTOPTS)
            DllStructSetData($tDTTOPTS, 'Size', DllStructGetSize($tDTTOPTS))
            DllStructSetData($tDTTOPTS, 'Flags', BitOR($DTT_TEXTCOLOR, $DTT_COMPOSITED))
            DllStructSetData($tDTTOPTS, 'clrText', 0xFFFFFF)

            ; Setup font
            Local $hFont = _SendMessage($hWnd, $WM_GETFONT, 0, 0)
            If Not $hFont Then $hFont = _WinAPI_GetStockObject($DEFAULT_GUI_FONT)
            Local $hOldFont = _WinAPI_SelectObject($hMemDC, $hFont)

            ;_WinAPI_SetBkMode($hDC, $TRANSPARENT)
            ;_WinAPI_SetTextColor($hDC, _WinAPI_SwitchColor(0xFFFFFF))

            ;Local $tTextRect = DllStructCreate($tagRECT)

            $tTextRect.Left = $tTextRect.Left + $tSIZE.X + 3
            ;$tTextRect.Top = $tTextRect.Top
            ;$tTextRect.Right = $tTextRect.Right
            ;$tTextRect.Bottom = $tTextRect.Bottom

            _WinAPI_DrawThemeTextEx($hTheme, $BP_CHECKBOX, $iStateID, $hMemDC, $sText, $tTextRect, $DT_SINGLELINE, $tDTTOPTS)

            ;DllCall("user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sText, "int", -1, "struct*", $tTextRect, "uint", BitOR($DT_CENTER, $DT_VCENTER, $DT_SINGLELINE, $DT_NOPREFIX, $DT_NOCLIP))

            _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hMemDC, 0, 0, $SRCCOPY)

            ; cleanup
            _WinAPI_SelectObject($hDC, $hOldFont)
            _WinAPI_CloseThemeData($hTheme)

            _WinAPI_SelectObject($hMemDC, $hOldBmp)
            _WinAPI_DeleteObject($hBitmap)
            _WinAPI_DeleteDC($hMemDC)

            _WinAPI_EndPaint($hWnd, $tPaint)
            Return 0

    EndSwitch

    Return __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_CheckboxProc

Func __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
    Return DllCall('comctl32.dll', 'lresult', 'DefSubclassProc', 'hwnd', $hWnd, 'uint', $iMsg, 'wparam', $wParam, _
            'lparam', $lParam)[0]
EndFunc   ;==>__WinAPI_DefSubclassProc

 

Posted

Since I move the text over based on the size of the checkbox, it looks like I also need to extend the text rect itself by that same amount since some is getting cut off.

The good thing is I can extend this for dark mode radio buttons on 24H2 and earlier OS builds as well.

Posted (edited)

Here is some progress with subclassing for both checkbox and radio buttons.

EDIT: I have WS_BORDER set on the checkbox control because I'm trying to get an idea on the sizing still. Lots of ConsoleWrite stuff going on. W.I.P.

#include <ButtonConstants.au3>
#include <WindowsStylesConstants.au3>
#include <GuiButton.au3>
#include <APIThemeConstants.au3>
#include <WinAPITheme.au3>
#include <WinAPIGdi.au3>
#include <APIGdiConstants.au3>
#include <StructureConstants.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPISysInternals.au3>
#include <WinAPIShellEx.au3>
#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>

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

Global Const $BP_RADIOBUTTON = 2
Global Const $BP_CHECKBOX = 3

Example()

Func Example()
    Local $sStyles
    ; Create a GUI with various controls.
    Local $hGUI = GUICreate("Example", 300, 200)
    GUISetBkColor("0x202020")

    ; Register Subclassing / Window Procedure
    Local $hSubclass = DllCallbackRegister(_CheckboxProc, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")

    ; Create a checkbox control.
    Local $idCheckbox = GUICtrlCreateCheckbox("Standard Checkbox", 40, 40, 185, 25, $WS_BORDER)
    Local $idRadio1 = GUICtrlCreateRadio("Radio 1", 40, 80, 120, 20)
    Local $idRadio2 = GUICtrlCreateRadio("Radio 2", 40, 120, 120, 20)
    Local $idButton_Close = GUICtrlCreateButton("Close", 210, 170, 85, 25)

    Local $hCheckbox = GUICtrlGetHandle($idCheckbox)
    $sStyles = "CHECKBOX"
    _WinAPI_SetWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox, $BP_CHECKBOX)

    Local $hRadio1= GUICtrlGetHandle($idRadio1)
    $sStyles = "BS_AUTORADIOBUTTON"
    _WinAPI_SetWindowSubclass($hRadio1, DllCallbackGetPtr($hSubclass), $idRadio1, $BP_RADIOBUTTON)

    Local $hRadio2 = GUICtrlGetHandle($idRadio2)
    $sStyles = "BS_AUTORADIOBUTTON"
    _WinAPI_SetWindowSubclass($hRadio2, DllCallbackGetPtr($hSubclass), $idRadio2, $BP_RADIOBUTTON)

    ; Display the GUI.
    GUISetState(@SW_SHOW, $hGUI)

    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idButton_Close
                ExitLoop

            Case $idCheckbox
                If _IsChecked($idCheckbox) Then
                    ;MsgBox($MB_SYSTEMMODAL, "", "The checkbox is checked.", 0, $hGUI)
                    ConsoleWrite("The checkbox is checked." & @CRLF)
                Else
                    ;MsgBox($MB_SYSTEMMODAL, "", "The checkbox is not checked.", 0, $hGUI)
                    ConsoleWrite("The checkbox is not checked." & @CRLF)
                EndIf

        EndSwitch
    WEnd

    ; Cleanup: Restore original Window Procedure
    _WinAPI_RemoveWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox)
    _WinAPI_RemoveWindowSubclass($hRadio1, DllCallbackGetPtr($hSubclass), $idRadio1)
    _WinAPI_RemoveWindowSubclass($hRadio2, DllCallbackGetPtr($hSubclass), $idRadio2)
    DllCallbackFree($hSubclass)

    ; Delete the previous GUI and all controls.
    GUIDelete($hGUI)
EndFunc   ;==>Example

Func _IsChecked($idControlID)
    Return BitAND(GUICtrlRead($idControlID), $GUI_CHECKED) = $GUI_CHECKED
EndFunc   ;==>_IsChecked

Func _CheckboxProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Local Static $bHover, $tPoint
    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1 ; Prevent background erasing to avoid flickering

        ;Case $WM_PRINTCLIENT

        Case $WM_PAINT
            ConsoleWrite("pData: " & $pData & @CRLF)
            Local $iStateID = 0
            Local $iPartID = $pData

            Local Const $BST_HOT = 0x0200

            ; checkbox StateID
            Local Const $CBS_UNCHECKEDNORMAL = 1
            Local Const $CBS_UNCHECKEDHOT = 2
            Local Const $CBS_UNCHECKEDPRESSED = 3
            Local Const $CBS_UNCHECKEDDISABLED = 4
            Local Const $CBS_CHECKEDNORMAL = 5
            Local Const $CBS_CHECKEDHOT = 6
            Local Const $CBS_CHECKEDPRESSED = 7
            Local Const $CBS_CHECKEDDISABLED = 8
            Local Const $CBS_MIXEDNORMAL = 9
            Local Const $CBS_MIXEDHOT = 10
            Local Const $CBS_MIXEDPRESSED = 11
            Local Const $CBS_MIXEDDISABLED = 12

            ; radio StateID
            Local Const $RBS_UNCHECKEDNORMAL = 1
            Local Const $RBS_UNCHECKEDHOT = 2
            Local Const $RBS_UNCHECKEDPRESSED = 3
            Local Const $RBS_UNCHECKEDDISABLED = 4
            Local Const $RBS_CHECKEDNORMAL = 5
            Local Const $RBS_CHECKEDHOT = 6
            Local Const $RBS_CHECKEDPRESSED = 7
            Local Const $RBS_CHECKEDDISABLED = 8

            Local $hTheme = _WinAPI_OpenThemeData($hWnd, "DarkMode_Explorer::Button")

            Local $tPaint = DllStructCreate($tagPAINTSTRUCT)
            Local $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)

            Local $tClient = _WinAPI_GetClientRect($hWnd)
            Local $iW = $tClient.Right
            Local $iH = $tClient.Bottom

            ; Initiate double buffering
            Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
            Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iW, $iH)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

            Local $iState = _GUICtrlButton_GetState($hWnd)
            ;ConsoleWrite("checkbox state: " & $iState & @CRLF)
            ;ConsoleWrite("$BST_FOCUS: " & $BST_FOCUS & @CRLF)
            #cs
            If BitAND($iState, $BST_HOT) Then ConsoleWrite("BST_HOT" & @CRLF)
            If BitAND($iState, $BST_INDETERMINATE) Then ConsoleWrite("BST_INDETERMINATE" & @CRLF)
            If BitAND($iState, $BST_UNCHECKED) Then ConsoleWrite("BST_UNCHECKED" & @CRLF)
            If BitAND($iState, $BST_PUSHED) Then
                ConsoleWrite("BST_PUSHED" & @CRLF)
                $iStateID = $CBS_UNCHECKEDPRESSED
            EndIf
            If BitAND($iState, $BST_DONTCLICK) Then ConsoleWrite("BST_DONTCLICK" & @CRLF)
            #ce

            If Not _WinAPI_IsWindowEnabled($hWnd) Then
                $iStateID = $CBS_UNCHECKEDDISABLED
            ElseIf BitAND($iState, $BST_PUSHED) Then
                ConsoleWrite("BST_PUSHED" & @CRLF)
                $iStateID = $CBS_UNCHECKEDPRESSED
            ElseIf BitAND($iState, $BST_HOT) Then
                ConsoleWrite("BST_HOT" & @CRLF)
                $iStateID = $CBS_UNCHECKEDHOT
            Else
                ConsoleWrite("CBS_UNCHECKEDNORMAL" & @CRLF)
                $iStateID = $CBS_UNCHECKEDNORMAL
            EndIf

            If BitAND($iState, $BST_CHECKED) Then
                ConsoleWrite("BST_CHECKED" & @CRLF)
                $iStateID = $CBS_CHECKEDNORMAL
            ElseIf BitAND($iState, $BST_INDETERMINATE) Then
                ConsoleWrite("BST_INDETERMINATE" & @CRLF)
                $iStateID = $CBS_MIXEDNORMAL
            EndIf

            ; GetThemeBackgroundContentRect
            Local $tTextRect = _WinAPI_GetThemeBackgroundContentRect($hTheme, $iPartID, $iStateID, $hMemDC, $tClient)
            Local $tSIZE = _WinAPI_GetThemePartSize($hTheme, $iPartID, 0, Null, Null, $TS_TRUE)

            ;Local $tRectClient = _WinAPI_GetClientRect($hWnd)

            Local $sText = _WinAPI_GetWindowText($hWnd)

            ; DrawThemeBackground
            Local $tRect = _WinAPI_CreateRectEx(0, 0, $tSIZE.X, $tSIZE.Y)
            _WinAPI_DrawThemeParentBackground($hWnd, $hMemDC, $tClient)
            _WinAPI_DrawThemeBackground($hTheme, $iPartID, $iStateID, $hMemDC, $tRect)

            ; Setup theme font
            ;Local $tLOGFONT = _WinAPI_GetThemeFont($hTheme, $BP_CHECKBOX, $CBS_CHECKEDNORMAL, $TMT_FONT)
            ;Local $hFont = _WinAPI_CreateFontIndirect($tLOGFONT)
            ;Local $hOldFont = _WinAPI_SelectObject($hDC, $hFont)

            Local $tDTTOPTS = DllStructCreate($tagDTTOPTS)
            DllStructSetData($tDTTOPTS, 'Size', DllStructGetSize($tDTTOPTS))
            DllStructSetData($tDTTOPTS, 'Flags', BitOR($DTT_TEXTCOLOR, $DTT_COMPOSITED))
            DllStructSetData($tDTTOPTS, 'clrText', 0xFFFFFF)

            ; Setup font
            Local $hFont = _SendMessage($hWnd, $WM_GETFONT, 0, 0)
            If Not $hFont Then $hFont = _WinAPI_GetStockObject($DEFAULT_GUI_FONT)
            Local $hOldFont = _WinAPI_SelectObject($hMemDC, $hFont)

            _WinAPI_SetBkMode($hMemDC, $TRANSPARENT)
            _WinAPI_SetTextColor($hMemDC, _WinAPI_SwitchColor(0xFFFFFF))

            ;Local $tTextRect = DllStructCreate($tagRECT)

            $tTextRect.Left = $tTextRect.Left + $tSIZE.X + 3
            ;$tTextRect.Top = $tTextRect.Top
            $tTextRect.Right = $tTextRect.Right + $tSIZE.X + 3
            ;$tTextRect.Bottom = $tTextRect.Bottom

            ;_WinAPI_DrawThemeTextEx($hTheme, $BP_CHECKBOX, $iStateID, $hMemDC, $sText, $tTextRect, BitOR($DT_SINGLELINE, $DT_NOCLIP), $tDTTOPTS)

            DllCall("user32.dll", "int", "DrawTextW", "handle", $hMemDC, "wstr", $sText, "int", -1, "struct*", $tTextRect, "uint", BitOR($DT_SINGLELINE, $DT_NOCLIP))

            _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hMemDC, 0, 0, $SRCCOPY)

            ; cleanup
            _WinAPI_SelectObject($hDC, $hOldFont)
            _WinAPI_CloseThemeData($hTheme)

            _WinAPI_SelectObject($hMemDC, $hOldBmp)
            _WinAPI_DeleteObject($hBitmap)
            _WinAPI_DeleteDC($hMemDC)

            _WinAPI_EndPaint($hWnd, $tPaint)
            Return 0

    EndSwitch

    Return __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_CheckboxProc

Func __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
    Return DllCall('comctl32.dll', 'lresult', 'DefSubclassProc', 'hwnd', $hWnd, 'uint', $iMsg, 'wparam', $wParam, _
            'lparam', $lParam)[0]
EndFunc   ;==>__WinAPI_DefSubclassProc

 

Edited by WildByDesign
Posted

Can anyone please test this latest script for alignment?

#include <ButtonConstants.au3>
#include <WindowsStylesConstants.au3>
#include <GuiButton.au3>
#include <APIThemeConstants.au3>
#include <WinAPITheme.au3>
#include <WinAPIGdi.au3>
#include <APIGdiConstants.au3>
#include <StructureConstants.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPISysInternals.au3>
#include <WinAPIShellEx.au3>
#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>

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

Global Const $BP_RADIOBUTTON = 2
Global Const $BP_CHECKBOX = 3

Example()

Func Example()
    ; Create a GUI with various controls.
    Local $hGUI = GUICreate("Example", 300, 200)
    GUISetBkColor("0x202020")

    ; Register Subclassing / Window Procedure
    Local $hSubclass = DllCallbackRegister(_CheckboxProc, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")

    ; Create a checkbox control.
    Local $idCheckbox = GUICtrlCreateCheckbox("Standard Checkbox", 40, 40)
    Local $hCheckbox = GUICtrlGetHandle(-1)
    Local $idRadio1 = GUICtrlCreateRadio("Radio 1", 40, 80)
    Local $hRadio1= GUICtrlGetHandle(-1)
    Local $idRadio2 = GUICtrlCreateRadio("Radio 2", 40, 120)
    Local $hRadio2 = GUICtrlGetHandle(-1)
    Local $idButton_Close = GUICtrlCreateButton("Close", 210, 160)
    _WinAPI_SetWindowTheme(GUICtrlGetHandle(-1), "DarkMode_Explorer")

    ; Get checkbox/radio button part size to increase width of controls plus padding
    Local $hTheme = _WinAPI_OpenThemeData($hGUI, "DarkMode_Explorer::Button")
    Local $tSIZE = _WinAPI_GetThemePartSize($hTheme, $BP_CHECKBOX, 0, Null, Null, $TS_TRUE)
    Local $iResize = $tSIZE.X + 3

    ; Increase the width of controls based on part size plus padding
    Local $aPos = WinGetPos($hCheckbox)
    If Not @error Then _WinAPI_SetWindowPos($hCheckbox, 0, $aPos[0], $aPos[1], $aPos[2] + $iResize, $aPos[3], $SWP_NOMOVE)
    Local $aPos = WinGetPos($hRadio1)
    If Not @error Then _WinAPI_SetWindowPos($hRadio1, 0, $aPos[0], $aPos[1], $aPos[2] + $iResize, $aPos[3], $SWP_NOMOVE)
    Local $aPos = WinGetPos($hRadio2)
    If Not @error Then _WinAPI_SetWindowPos($hRadio2, 0, $aPos[0], $aPos[1], $aPos[2] + $iResize, $aPos[3], $SWP_NOMOVE)

    ; Subclass controls
    _WinAPI_SetWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox, $BP_CHECKBOX)
    _WinAPI_SetWindowSubclass($hRadio1, DllCallbackGetPtr($hSubclass), $idRadio1, $BP_RADIOBUTTON)
    _WinAPI_SetWindowSubclass($hRadio2, DllCallbackGetPtr($hSubclass), $idRadio2, $BP_RADIOBUTTON)

    ; Display the GUI
    GUISetState(@SW_SHOW, $hGUI)

    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idButton_Close
                ExitLoop
        EndSwitch
    WEnd

    ; Cleanup: Restore original Window Procedure
    _WinAPI_RemoveWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox)
    _WinAPI_RemoveWindowSubclass($hRadio1, DllCallbackGetPtr($hSubclass), $idRadio1)
    _WinAPI_RemoveWindowSubclass($hRadio2, DllCallbackGetPtr($hSubclass), $idRadio2)
    DllCallbackFree($hSubclass)

    ; Close theme data
    If $hTheme Then _WinAPI_CloseThemeData($hTheme)

    ; Delete the previous GUI and all controls.
    GUIDelete($hGUI)
EndFunc   ;==>Example

Func _CheckboxProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Local Static $bHover, $tPoint
    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1 ; Prevent background erasing to avoid flickering

        Case $WM_PAINT
            Local $iStateID = 0
            Local $iPartID = $pData

            Local Const $BST_HOT = 0x0200

            ; checkbox StateID
            Local Const $CBS_UNCHECKEDNORMAL = 1
            Local Const $CBS_UNCHECKEDHOT = 2
            Local Const $CBS_UNCHECKEDPRESSED = 3
            Local Const $CBS_UNCHECKEDDISABLED = 4
            Local Const $CBS_CHECKEDNORMAL = 5
            Local Const $CBS_CHECKEDHOT = 6
            Local Const $CBS_CHECKEDPRESSED = 7
            Local Const $CBS_CHECKEDDISABLED = 8
            Local Const $CBS_MIXEDNORMAL = 9
            Local Const $CBS_MIXEDHOT = 10
            Local Const $CBS_MIXEDPRESSED = 11
            Local Const $CBS_MIXEDDISABLED = 12

            ; radio StateID
            Local Const $RBS_UNCHECKEDNORMAL = 1
            Local Const $RBS_UNCHECKEDHOT = 2
            Local Const $RBS_UNCHECKEDPRESSED = 3
            Local Const $RBS_UNCHECKEDDISABLED = 4
            Local Const $RBS_CHECKEDNORMAL = 5
            Local Const $RBS_CHECKEDHOT = 6
            Local Const $RBS_CHECKEDPRESSED = 7
            Local Const $RBS_CHECKEDDISABLED = 8

            Local $hTheme = _WinAPI_OpenThemeData($hWnd, "DarkMode_Explorer::Button")

            Local $tPaint = DllStructCreate($tagPAINTSTRUCT)
            Local $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)

            Local $tClient = _WinAPI_GetClientRect($hWnd)
            Local $iW = $tClient.Right
            Local $iH = $tClient.Bottom

            ; Initiate double buffering
            Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
            Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iW, $iH)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

            Local $iState = _GUICtrlButton_GetState($hWnd)

            Switch $iPartID
                Case $BP_CHECKBOX
                    If Not _WinAPI_IsWindowEnabled($hWnd) Then
                        $iStateID = $CBS_UNCHECKEDDISABLED
                    ElseIf BitAND($iState, $BST_PUSHED) Then
                        $iStateID = $CBS_UNCHECKEDPRESSED
                    ElseIf BitAND($iState, $BST_HOT) Then
                        $iStateID = $CBS_UNCHECKEDHOT
                    Else
                        $iStateID = $CBS_UNCHECKEDNORMAL
                    EndIf

                    If BitAND($iState, $BST_CHECKED) Then
                        $iStateID = $CBS_CHECKEDNORMAL
                    ElseIf BitAND($iState, $BST_INDETERMINATE) Then
                        $iStateID = $CBS_MIXEDNORMAL
                    EndIf

                Case $BP_RADIOBUTTON
                    If Not _WinAPI_IsWindowEnabled($hWnd) Then
                        $iStateID = $RBS_UNCHECKEDDISABLED
                    ElseIf BitAND($iState, $BST_PUSHED) Then
                        $iStateID = $RBS_UNCHECKEDPRESSED
                    ElseIf BitAND($iState, $BST_HOT) Then
                        $iStateID = $RBS_UNCHECKEDHOT
                    Else
                        $iStateID = $RBS_UNCHECKEDNORMAL
                    EndIf

                    If BitAND($iState, $BST_CHECKED) Then
                        $iStateID = $RBS_CHECKEDNORMAL
                    EndIf
            EndSwitch

            ; GetThemeBackgroundContentRect
            Local $tTextRect = _WinAPI_GetThemeBackgroundContentRect($hTheme, $iPartID, $iStateID, $hMemDC, $tClient)
            Local $tSIZE = _WinAPI_GetThemePartSize($hTheme, $iPartID, 0, Null, Null, $TS_TRUE)

            Local $sText = _WinAPI_GetWindowText($hWnd)

            ; DrawThemeBackground
            Local $tRect = _WinAPI_CreateRectEx(0, 0, $tSIZE.X, $tSIZE.Y)
            _WinAPI_DrawThemeParentBackground($hWnd, $hMemDC, $tClient)
            _WinAPI_DrawThemeBackground($hTheme, $iPartID, $iStateID, $hMemDC, $tRect)

            ; Setup theme font
            ;Local $tLOGFONT = _WinAPI_GetThemeFont($hTheme, $BP_CHECKBOX, $CBS_CHECKEDNORMAL, $TMT_FONT)
            ;Local $hFont = _WinAPI_CreateFontIndirect($tLOGFONT)
            ;Local $hOldFont = _WinAPI_SelectObject($hDC, $hFont)

            Local $tDTTOPTS = DllStructCreate($tagDTTOPTS)
            DllStructSetData($tDTTOPTS, 'Size', DllStructGetSize($tDTTOPTS))
            DllStructSetData($tDTTOPTS, 'Flags', BitOR($DTT_TEXTCOLOR, $DTT_COMPOSITED))
            DllStructSetData($tDTTOPTS, 'clrText', 0xFFFFFF)

            ; Setup font
            Local $hFont = _SendMessage($hWnd, $WM_GETFONT, 0, 0)
            If Not $hFont Then $hFont = _WinAPI_GetStockObject($DEFAULT_GUI_FONT)
            Local $hOldFont = _WinAPI_SelectObject($hMemDC, $hFont)

            _WinAPI_SetBkMode($hMemDC, $TRANSPARENT)
            _WinAPI_SetTextColor($hMemDC, _WinAPI_SwitchColor(0xFFFFFF))

            ;Local $tTextRect = DllStructCreate($tagRECT)

            $tTextRect.Left = $tTextRect.Left + $tSIZE.X + 3
            ;$tTextRect.Top = $tTextRect.Top
            $tTextRect.Right = $tTextRect.Right + $tSIZE.X + 3
            ;$tTextRect.Bottom = $tTextRect.Bottom

            ;_WinAPI_DrawThemeTextEx($hTheme, $BP_CHECKBOX, $iStateID, $hMemDC, $sText, $tTextRect, BitOR($DT_SINGLELINE, $DT_NOCLIP), $tDTTOPTS)

            DllCall("user32.dll", "int", "DrawTextW", "handle", $hMemDC, "wstr", $sText, "int", -1, "struct*", $tTextRect, "uint", BitOR($DT_SINGLELINE, $DT_NOCLIP))

            _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hMemDC, 0, 0, $SRCCOPY)

            ; cleanup
            _WinAPI_SelectObject($hDC, $hOldFont)
            _WinAPI_CloseThemeData($hTheme)

            _WinAPI_SelectObject($hMemDC, $hOldBmp)
            _WinAPI_DeleteObject($hBitmap)
            _WinAPI_DeleteDC($hMemDC)

            _WinAPI_EndPaint($hWnd, $tPaint)
            Return 0

    EndSwitch

    Return __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_CheckboxProc

Func __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
    Return DllCall('comctl32.dll', 'lresult', 'DefSubclassProc', 'hwnd', $hWnd, 'uint', $iMsg, 'wparam', $wParam, _
            'lparam', $lParam)[0]
EndFunc   ;==>__WinAPI_DefSubclassProc

 

Posted

I did some math to make sure that the checkbox/radio button is vertically aligned within the control rectangle and same for the DrawThemeTextEx. I added WS_BORDER for all 3 controls just to get an idea on how well the alignment is working just for testing purposes. I increased the height of some of the controls just for testing purposes.

I think I'm getting closer to this being usable in GUIDarkTheme UDF for Windows 10 to Windows 11 24H2 users. 25H2+ wont need the subclass, so I would only enable it on the affected systems in the UDF. But for testing purposes, we can still test it on 25H2.

@argumentum Can you please test this?

Also, you could even add in GUISetFont(10) or GUISetFont(12) before the controls are created to see how different font sizes behave. So far so good on my end.

#include <ButtonConstants.au3>
#include <WindowsStylesConstants.au3>
#include <GuiButton.au3>
#include <APIThemeConstants.au3>
#include <WinAPITheme.au3>
#include <WinAPIGdi.au3>
#include <APIGdiConstants.au3>
#include <StructureConstants.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPISysInternals.au3>
#include <WinAPIShellEx.au3>
#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>

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

Global Const $BP_RADIOBUTTON = 2
Global Const $BP_CHECKBOX = 3

Example()

Func Example()
    ; Create a GUI with various controls.
    Local $hGUI = GUICreate("Example", 300, 200)
    GUISetBkColor("0x202020")

    ; Register Subclassing / Window Procedure
    Local $hSubclass = DllCallbackRegister(_CheckboxProc, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")

    ; Create a checkbox control.
    Local $idCheckbox = GUICtrlCreateCheckbox("Standard Checkbox", 40, 20, -1, 50, $WS_BORDER)
    Local $hCheckbox = GUICtrlGetHandle(-1)
    Local $idRadio1 = GUICtrlCreateRadio("Radio 1", 40, 80, -1, -1, $WS_BORDER)
    Local $hRadio1= GUICtrlGetHandle(-1)
    Local $idRadio2 = GUICtrlCreateRadio("Radio 2", 40, 120, -1, 60, $WS_BORDER)
    Local $hRadio2 = GUICtrlGetHandle(-1)
    Local $idButton_Close = GUICtrlCreateButton("Close", 210, 160)
    _WinAPI_SetWindowTheme(GUICtrlGetHandle(-1), "DarkMode_Explorer")

    ; Get checkbox/radio button part size to increase width of controls plus padding
    Local $hTheme = _WinAPI_OpenThemeData($hGUI, "DarkMode_Explorer::Button")
    Local $tSIZE = _WinAPI_GetThemePartSize($hTheme, $BP_CHECKBOX, 0, Null, Null, $TS_TRUE)
    Local $iResize = $tSIZE.X + 3

    ; Increase the width of controls based on part size plus padding
    Local $aPos = WinGetPos($hCheckbox)
    If Not @error Then _WinAPI_SetWindowPos($hCheckbox, 0, $aPos[0], $aPos[1], $aPos[2] + $iResize, $aPos[3], $SWP_NOMOVE)
    Local $aPos = WinGetPos($hRadio1)
    If Not @error Then _WinAPI_SetWindowPos($hRadio1, 0, $aPos[0], $aPos[1], $aPos[2] + $iResize, $aPos[3], $SWP_NOMOVE)
    Local $aPos = WinGetPos($hRadio2)
    If Not @error Then _WinAPI_SetWindowPos($hRadio2, 0, $aPos[0], $aPos[1], $aPos[2] + $iResize, $aPos[3], $SWP_NOMOVE)

    ; Subclass controls
    _WinAPI_SetWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox, $BP_CHECKBOX)
    _WinAPI_SetWindowSubclass($hRadio1, DllCallbackGetPtr($hSubclass), $idRadio1, $BP_RADIOBUTTON)
    _WinAPI_SetWindowSubclass($hRadio2, DllCallbackGetPtr($hSubclass), $idRadio2, $BP_RADIOBUTTON)

    ; Display the GUI
    GUISetState(@SW_SHOW, $hGUI)

    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idButton_Close
                ExitLoop
        EndSwitch
    WEnd

    ; Cleanup: Restore original Window Procedure
    _WinAPI_RemoveWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox)
    _WinAPI_RemoveWindowSubclass($hRadio1, DllCallbackGetPtr($hSubclass), $idRadio1)
    _WinAPI_RemoveWindowSubclass($hRadio2, DllCallbackGetPtr($hSubclass), $idRadio2)
    DllCallbackFree($hSubclass)

    ; Close theme data
    If $hTheme Then _WinAPI_CloseThemeData($hTheme)

    ; Delete the previous GUI and all controls.
    GUIDelete($hGUI)
EndFunc   ;==>Example

Func _CheckboxProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Local Static $bHover, $tPoint
    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1 ; Prevent background erasing to avoid flickering

        Case $WM_PAINT
            Local $hTheme = _WinAPI_OpenThemeData($hWnd, "DarkMode_Explorer::Button")
            Local $iStateID = 0
            Local $iPartID = $pData

            Local Const $BST_HOT = 0x0200

            ; checkbox StateID
            Local Const $CBS_UNCHECKEDNORMAL = 1
            Local Const $CBS_UNCHECKEDHOT = 2
            Local Const $CBS_UNCHECKEDPRESSED = 3
            Local Const $CBS_UNCHECKEDDISABLED = 4
            Local Const $CBS_CHECKEDNORMAL = 5
            Local Const $CBS_CHECKEDHOT = 6
            Local Const $CBS_CHECKEDPRESSED = 7
            Local Const $CBS_CHECKEDDISABLED = 8
            Local Const $CBS_MIXEDNORMAL = 9
            Local Const $CBS_MIXEDHOT = 10
            Local Const $CBS_MIXEDPRESSED = 11
            Local Const $CBS_MIXEDDISABLED = 12

            ; radio StateID
            Local Const $RBS_UNCHECKEDNORMAL = 1
            Local Const $RBS_UNCHECKEDHOT = 2
            Local Const $RBS_UNCHECKEDPRESSED = 3
            Local Const $RBS_UNCHECKEDDISABLED = 4
            Local Const $RBS_CHECKEDNORMAL = 5
            Local Const $RBS_CHECKEDHOT = 6
            Local Const $RBS_CHECKEDPRESSED = 7
            Local Const $RBS_CHECKEDDISABLED = 8

            Local $tPaint = DllStructCreate($tagPAINTSTRUCT)
            Local $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)

            Local $tClient = _WinAPI_GetClientRect($hWnd)
            Local $iW = $tClient.Right
            Local $iH = $tClient.Bottom

            ; Initiate double buffering
            Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
            Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iW, $iH)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

            Local $iState = _GUICtrlButton_GetState($hWnd)

            Switch $iPartID
                Case $BP_CHECKBOX
                    If Not _WinAPI_IsWindowEnabled($hWnd) Then
                        $iStateID = $CBS_UNCHECKEDDISABLED
                    ElseIf BitAND($iState, $BST_PUSHED) Then
                        $iStateID = $CBS_UNCHECKEDPRESSED
                    ElseIf BitAND($iState, $BST_HOT) Then
                        $iStateID = $CBS_UNCHECKEDHOT
                    Else
                        $iStateID = $CBS_UNCHECKEDNORMAL
                    EndIf

                    If BitAND($iState, $BST_CHECKED) Then
                        $iStateID = $CBS_CHECKEDNORMAL
                    ElseIf BitAND($iState, $BST_INDETERMINATE) Then
                        $iStateID = $CBS_MIXEDNORMAL
                    EndIf

                Case $BP_RADIOBUTTON
                    If Not _WinAPI_IsWindowEnabled($hWnd) Then
                        $iStateID = $RBS_UNCHECKEDDISABLED
                    ElseIf BitAND($iState, $BST_PUSHED) Then
                        $iStateID = $RBS_UNCHECKEDPRESSED
                    ElseIf BitAND($iState, $BST_HOT) Then
                        $iStateID = $RBS_UNCHECKEDHOT
                    Else
                        $iStateID = $RBS_UNCHECKEDNORMAL
                    EndIf

                    If BitAND($iState, $BST_CHECKED) Then
                        $iStateID = $RBS_CHECKEDNORMAL
                    EndIf
            EndSwitch

            ; GetThemeBackgroundContentRect
            Local $tTextRect = _WinAPI_GetThemeBackgroundContentRect($hTheme, $iPartID, $iStateID, $hMemDC, $tClient)
            Local $tSIZE = _WinAPI_GetThemePartSize($hTheme, $iPartID, 0, Null, Null, $TS_TRUE)

            Local $sText = _WinAPI_GetWindowText($hWnd)

            ;DrawThemeParentBackground
            _WinAPI_DrawThemeParentBackground($hWnd, $hMemDC, $tClient)

            ; DrawThemeBackground
            Local $iWidth = $tSIZE.X
            Local $iHeight = $tSIZE.Y
            Local $iClientHeight = $tClient.Bottom - $tClient.Top
            Local $iHeightPad = ($iClientHeight - $iHeight)
            Local $tRect = _WinAPI_CreateRectEx(0, 0, $iWidth, $iHeight + $iHeightPad)
            _WinAPI_DrawThemeBackground($hTheme, $iPartID, $iStateID, $hMemDC, $tRect)

            ; Setup theme font
            ;Local $tLOGFONT = _WinAPI_GetThemeFont($hTheme, $BP_CHECKBOX, $CBS_CHECKEDNORMAL, $TMT_FONT)
            ;Local $hFont = _WinAPI_CreateFontIndirect($tLOGFONT)
            ;Local $hOldFont = _WinAPI_SelectObject($hDC, $hFont)

            Local $tDTTOPTS = DllStructCreate($tagDTTOPTS)
            DllStructSetData($tDTTOPTS, 'Size', DllStructGetSize($tDTTOPTS))
            DllStructSetData($tDTTOPTS, 'Flags', BitOR($DTT_TEXTCOLOR, $DTT_COMPOSITED))
            DllStructSetData($tDTTOPTS, 'clrText', 0xFFFFFF)

            ; Setup font
            Local $hFont = _SendMessage($hWnd, $WM_GETFONT, 0, 0)
            If Not $hFont Then $hFont = _WinAPI_GetStockObject($DEFAULT_GUI_FONT)
            Local $hOldFont = _WinAPI_SelectObject($hMemDC, $hFont)

            _WinAPI_SetBkMode($hMemDC, $TRANSPARENT)
            _WinAPI_SetTextColor($hMemDC, _WinAPI_SwitchColor(0xFFFFFF))

            $tTextRect.Left = $tTextRect.Left + $tSIZE.X + 3
            $tTextRect.Right = $tTextRect.Right + $tSIZE.X + 3

            _WinAPI_DrawThemeTextEx($hTheme, $BP_CHECKBOX, $iStateID, $hMemDC, $sText, $tTextRect, BitOR($DT_SINGLELINE, $DT_NOCLIP, $DT_VCENTER), $tDTTOPTS)

            _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hMemDC, 0, 0, $SRCCOPY)

            ; cleanup
            _WinAPI_CloseThemeData($hTheme)
            _WinAPI_SelectObject($hDC, $hOldFont)
            _WinAPI_SelectObject($hMemDC, $hOldBmp)
            _WinAPI_DeleteObject($hBitmap)
            _WinAPI_DeleteDC($hMemDC)

            _WinAPI_EndPaint($hWnd, $tPaint)
            Return 0

    EndSwitch

    Return __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_CheckboxProc

Func __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
    Return DllCall('comctl32.dll', 'lresult', 'DefSubclassProc', 'hwnd', $hWnd, 'uint', $iMsg, 'wparam', $wParam, _
            'lparam', $lParam)[0]
EndFunc   ;==>__WinAPI_DefSubclassProc

 

  • Solution
Posted

Latest version of the script adds support for parsing the styles to determine DrawThemeTextEx flags (DT_*) needed to handle checkbox/radio button styles such as BS_RIGHT, BS_CENTER and more. I need to do more work on those styles though. But it's a step in the right direction. I've cleaned up the script quite a bit as well.

#include <ButtonConstants.au3>
#include <WindowsStylesConstants.au3>
#include <GuiButton.au3>
#include <APIThemeConstants.au3>
#include <WinAPITheme.au3>
#include <WinAPIGdi.au3>
#include <APIGdiConstants.au3>
#include <StructureConstants.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPISysInternals.au3>
#include <WinAPIShellEx.au3>
#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>

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

Global Const $BP_RADIOBUTTON = 2
Global Const $BP_CHECKBOX = 3

Example()

Func Example()
    ; Create a GUI with various controls.
    Local $hGUI = GUICreate("Example", 300, 200)
    GUISetBkColor("0x202020")

    ; Register Subclassing / Window Procedure
    Local $hSubclass = DllCallbackRegister(_ButtonProc, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")

    ; Create a checkbox control.
    Local $idCheckbox = GUICtrlCreateCheckbox("Standard Checkbox", 40, 40)
    Local $hCheckbox = GUICtrlGetHandle(-1)
    Local $idRadio1 = GUICtrlCreateRadio("Radio 1", 40, 80)
    Local $hRadio1= GUICtrlGetHandle(-1)
    Local $idRadio2 = GUICtrlCreateRadio("Radio 2", 40, 110)
    Local $hRadio2 = GUICtrlGetHandle(-1)
    Local $idButton_Close = GUICtrlCreateButton("Close", 210, 160)
    _WinAPI_SetWindowTheme(GUICtrlGetHandle(-1), "DarkMode_Explorer")

    ; Get checkbox/radio button part size to increase width of controls plus padding
    Local $hTheme = _WinAPI_OpenThemeData($hGUI, "DarkMode_Explorer::Button")
    Local $tSIZE = _WinAPI_GetThemePartSize($hTheme, $BP_CHECKBOX, 0, Null, Null, $TS_TRUE)
    Local $iResize = $tSIZE.X + 3
    _WinAPI_CloseThemeData($hTheme)

    ; Increase the width of controls based on part size plus padding
    Local $aPos = WinGetPos($hCheckbox)
    If Not @error Then _WinAPI_SetWindowPos($hCheckbox, 0, $aPos[0], $aPos[1], $aPos[2] + $iResize, $aPos[3], $SWP_NOMOVE)
    Local $aPos = WinGetPos($hRadio1)
    If Not @error Then _WinAPI_SetWindowPos($hRadio1, 0, $aPos[0], $aPos[1], $aPos[2] + $iResize, $aPos[3], $SWP_NOMOVE)
    Local $aPos = WinGetPos($hRadio2)
    If Not @error Then _WinAPI_SetWindowPos($hRadio2, 0, $aPos[0], $aPos[1], $aPos[2] + $iResize, $aPos[3], $SWP_NOMOVE)

    ; Subclass controls
    Local $sStyles = "BS_AUTOCHECKBOX, WS_CHILD, WS_TABSTOP"
    Local $tStruct1 = DllStructCreate("struct;int;wchar[256];endstruct")
    DllStructSetData($tStruct1, 1, $BP_CHECKBOX)
    DllStructSetData($tStruct1, 2, $sStyles)
    _WinAPI_SetWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox, DllStructGetPtr($tStruct1))

    Local $sStyles = "BS_AUTORADIOBUTTON, WS_CHILD, WS_VISIBLE, WS_TABSTOP"
    Local $tStruct2 = DllStructCreate("struct;int;wchar[256];endstruct")
    DllStructSetData($tStruct2, 1, $BP_RADIOBUTTON)
    DllStructSetData($tStruct2, 2, $sStyles)
    _WinAPI_SetWindowSubclass($hRadio1, DllCallbackGetPtr($hSubclass), $idRadio1, DllStructGetPtr($tStruct2))

    Local $sStyles = "BS_AUTORADIOBUTTON, WS_CHILD, WS_VISIBLE, WS_TABSTOP"
    Local $tStruct3 = DllStructCreate("struct;int;wchar[256];endstruct")
    DllStructSetData($tStruct3, 1, $BP_RADIOBUTTON)
    DllStructSetData($tStruct3, 2, $sStyles)
    _WinAPI_SetWindowSubclass($hRadio2, DllCallbackGetPtr($hSubclass), $idRadio2, DllStructGetPtr($tStruct3))

    ; Display the GUI
    GUISetState(@SW_SHOW, $hGUI)

    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idButton_Close
                ExitLoop
        EndSwitch
    WEnd

    ; Cleanup: Restore original Window Procedure
    _WinAPI_RemoveWindowSubclass($hCheckbox, DllCallbackGetPtr($hSubclass), $idCheckbox)
    _WinAPI_RemoveWindowSubclass($hRadio1, DllCallbackGetPtr($hSubclass), $idRadio1)
    _WinAPI_RemoveWindowSubclass($hRadio2, DllCallbackGetPtr($hSubclass), $idRadio2)
    DllCallbackFree($hSubclass)

    ; Delete the previous GUI and all controls.
    GUIDelete($hGUI)
EndFunc   ;==>Example

Func _ButtonProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Local Static $bHover, $tPoint
    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1 ; Prevent background erasing to avoid flickering

        Case $WM_PAINT
            Local $tStruct = DllStructCreate("struct;int;wchar[256];endstruct", $pData)
            Local $iPartID = DllStructGetData($tStruct, 1)
            Local $sStyles = DllStructGetData($tStruct, 2)

            Local $hTheme = _WinAPI_OpenThemeData($hWnd, "DarkMode_Explorer::Button")
            Local $iStateID = 0

            Local Const $BST_HOT = 0x0200

            ; checkbox StateID
            Local Const $CBS_UNCHECKEDNORMAL = 1
            Local Const $CBS_UNCHECKEDHOT = 2
            Local Const $CBS_UNCHECKEDPRESSED = 3
            Local Const $CBS_UNCHECKEDDISABLED = 4
            Local Const $CBS_CHECKEDNORMAL = 5
            Local Const $CBS_CHECKEDHOT = 6
            Local Const $CBS_CHECKEDPRESSED = 7
            Local Const $CBS_CHECKEDDISABLED = 8
            Local Const $CBS_MIXEDNORMAL = 9
            Local Const $CBS_MIXEDHOT = 10
            Local Const $CBS_MIXEDPRESSED = 11
            Local Const $CBS_MIXEDDISABLED = 12

            ; radio StateID
            Local Const $RBS_UNCHECKEDNORMAL = 1
            Local Const $RBS_UNCHECKEDHOT = 2
            Local Const $RBS_UNCHECKEDPRESSED = 3
            Local Const $RBS_UNCHECKEDDISABLED = 4
            Local Const $RBS_CHECKEDNORMAL = 5
            Local Const $RBS_CHECKEDHOT = 6
            Local Const $RBS_CHECKEDPRESSED = 7
            Local Const $RBS_CHECKEDDISABLED = 8

            Local $tPaint = DllStructCreate($tagPAINTSTRUCT)
            Local $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)

            Local $tClient = _WinAPI_GetClientRect($hWnd)
            Local $iW = $tClient.Right
            Local $iH = $tClient.Bottom

            ; Initiate double buffering
            Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
            Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iW, $iH)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

            Local $iState = _GUICtrlButton_GetState($hWnd)

            ; Determine StateID from current state
            Switch $iPartID
                Case $BP_CHECKBOX
                    If Not _WinAPI_IsWindowEnabled($hWnd) Then
                        $iStateID = $CBS_UNCHECKEDDISABLED
                    ElseIf BitAND($iState, $BST_PUSHED) Then
                        $iStateID = $CBS_UNCHECKEDPRESSED
                    ElseIf BitAND($iState, $BST_HOT) Then
                        $iStateID = $CBS_UNCHECKEDHOT
                    Else
                        $iStateID = $CBS_UNCHECKEDNORMAL
                    EndIf

                    If BitAND($iState, $BST_CHECKED) Then
                        $iStateID = $CBS_CHECKEDNORMAL
                    ElseIf BitAND($iState, $BST_INDETERMINATE) Then
                        $iStateID = $CBS_MIXEDNORMAL
                    EndIf

                Case $BP_RADIOBUTTON
                    If Not _WinAPI_IsWindowEnabled($hWnd) Then
                        $iStateID = $RBS_UNCHECKEDDISABLED
                    ElseIf BitAND($iState, $BST_PUSHED) Then
                        $iStateID = $RBS_UNCHECKEDPRESSED
                    ElseIf BitAND($iState, $BST_HOT) Then
                        $iStateID = $RBS_UNCHECKEDHOT
                    Else
                        $iStateID = $RBS_UNCHECKEDNORMAL
                    EndIf

                    If BitAND($iState, $BST_CHECKED) Then
                        $iStateID = $RBS_CHECKEDNORMAL
                    EndIf
            EndSwitch

            ; Determine DrawText $DT_* flags based on detected styles
            Local $iTextFlags
            Select
                Case StringInStr($sStyles, "BS_RIGHT") <> 0
                    $iTextFlags = BitOR($DT_SINGLELINE, $DT_NOCLIP, $DT_VCENTER, $DT_RIGHT)
                Case StringInStr($sStyles, "BS_CENTER") <> 0
                    $iTextFlags = BitOR($DT_SINGLELINE, $DT_NOCLIP, $DT_VCENTER, $DT_CENTER)
                Case Else
                    $iTextFlags = BitOR($DT_SINGLELINE, $DT_NOCLIP, $DT_VCENTER, $DT_LEFT)
            EndSelect

            ; GetThemeBackgroundContentRect
            Local $tTextRect = _WinAPI_GetThemeBackgroundContentRect($hTheme, $iPartID, $iStateID, $hMemDC, $tClient)
            Local $tSIZE = _WinAPI_GetThemePartSize($hTheme, $iPartID, 0, Null, Null, $TS_TRUE)

            ; Get text from control
            Local $sText = _WinAPI_GetWindowText($hWnd)

            ; DrawThemeParentBackground
            _WinAPI_DrawThemeParentBackground($hWnd, $hMemDC, $tClient)

            ; DrawThemeBackground
            Local $iWidth = $tSIZE.X
            Local $iHeight = $tSIZE.Y
            Local $iClientHeight = $tClient.Bottom - $tClient.Top
            Local $iHeightPad = ($iClientHeight - $iHeight)
            Local $tRect = _WinAPI_CreateRectEx(0, 0, $iWidth, $iHeight + $iHeightPad)
            _WinAPI_DrawThemeBackground($hTheme, $iPartID, $iStateID, $hMemDC, $tRect)

            ; Set flags for DTTOPTS structure
            Local $tDTTOPTS = DllStructCreate($tagDTTOPTS)
            DllStructSetData($tDTTOPTS, 'Size', DllStructGetSize($tDTTOPTS))
            DllStructSetData($tDTTOPTS, 'Flags', $DTT_TEXTCOLOR)
            DllStructSetData($tDTTOPTS, 'clrText', 0xFFFFFF)

            ; Setup font
            Local $hFont = _SendMessage($hWnd, $WM_GETFONT, 0, 0)
            If Not $hFont Then $hFont = _WinAPI_GetStockObject($DEFAULT_GUI_FONT)
            Local $hOldFont = _WinAPI_SelectObject($hMemDC, $hFont)

            $tTextRect.Left = $tTextRect.Left + $tSIZE.X + 3
            $tTextRect.Right = $tTextRect.Right + $tSIZE.X + 3

            _WinAPI_DrawThemeTextEx($hTheme, $BP_CHECKBOX, $iStateID, $hMemDC, $sText, $tTextRect, $iTextFlags, $tDTTOPTS)

            _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hMemDC, 0, 0, $SRCCOPY)

            ; Cleanup
            _WinAPI_CloseThemeData($hTheme)
            _WinAPI_SelectObject($hDC, $hOldFont)
            _WinAPI_SelectObject($hMemDC, $hOldBmp)
            _WinAPI_DeleteObject($hBitmap)
            _WinAPI_DeleteDC($hMemDC)

            _WinAPI_EndPaint($hWnd, $tPaint)
            Return 0

    EndSwitch

    Return __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_ButtonProc

Func __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
    Return DllCall('comctl32.dll', 'lresult', 'DefSubclassProc', 'hwnd', $hWnd, 'uint', $iMsg, 'wparam', $wParam, _
            'lparam', $lParam)[0]
EndFunc   ;==>__WinAPI_DefSubclassProc

 

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