Jump to content

Groupbox subclass (please help with Rect measurements)


Go to solution Solved by argumentum,

Recommended Posts

Posted (edited)

I have a Groupbox subclass that is working reasonably well, but there are some measurement/alignment issues that I really need help with.

This is needed for GUIDarkTheme UDF for versions of Windows prior to Windows 11 25H2 because on those affected systems we have to remove the theme. My hope is to give those users a nice looking dark mode Groupbox.

I am following along some C++ code and trying my best to understand it. Some of my variables are named the same as those just to help me follow along.

Thank you. :)

WM_PAINT: DmlibSubclassControl.cpp#L498

/**
 * @brief Paints a group box frame and text with custom colors.
 *
 * Handles drawing a themed group box with optional centered text, styled borders,
 * and font fallback. If a caption text is present, the frame is clipped to avoid overdrawing
 * behind the text. The function adapts layout for both centered and left-aligned titles.
 *
 * Paint logic:
 * - Determines current visual state (`GBS_DISABLED`, `GBS_NORMAL`).
 * - Retrieves themed font via `GetThemeFont` or falls back to dialog font.
 * - Measures caption text, computes layout and exclusion for frame clipping.
 * - Paints the outer rounded frame via @ref dmlib::paintRoundFrameRect
 *   using `dmlib::getEdgePen()`.
 * - Restores clip region and draws text using `DrawThemeTextEx` with custom colors.
 *
 * @param[in]   hWnd        Handle to the group box control.
 * @param[in]   hdc         Device context to draw into.
 * @param[in]   buttonData  Reference to the theming and state info (theme handle).
 *
 * @note Ensures proper cleanup of temporary GDI objects (font, clip region).
 *
 * @see dmlib::paintRoundFrameRect()
 */
static void paintGroupbox(HWND hWnd, HDC hdc, const dmlib_subclass::ButtonData& buttonData)
{
	const auto& hTheme = buttonData.m_themeData.getHTheme();

	// Style part

	const bool isDisabled = ::IsWindowEnabled(hWnd) == FALSE;
	static constexpr int iPartID = BP_GROUPBOX;
	const int iStateID = isDisabled ? GBS_DISABLED : GBS_NORMAL;

	// Font part

	bool isFontCreated = false;
	HFONT hFont = nullptr;
	LOGFONT lf{};
	if (SUCCEEDED(::GetThemeFont(hTheme, hdc, iPartID, iStateID, TMT_FONT, &lf)))
	{
		hFont = ::CreateFontIndirectW(&lf);
		isFontCreated = true;
	}

	if (hFont == nullptr)
	{
		hFont = reinterpret_cast<HFONT>(::SendMessage(hWnd, WM_GETFONT, 0, 0));
		isFontCreated = false;
	}

	const auto holdFont = dmlib_paint::GdiObject{ hdc, hFont, !isFontCreated };

	// Text rectangle part

	std::wstring buffer;
	const auto bufferLen = static_cast<size_t>(::GetWindowTextLengthW(hWnd));
	if (bufferLen > 0)
	{
		buffer.resize(bufferLen + 1, L'\0');
		::GetWindowTextW(hWnd, buffer.data(), static_cast<int>(buffer.length()));
	}

	const auto nStyle = ::GetWindowLongPtr(hWnd, GWL_STYLE);
	const bool isCenter = (nStyle & BS_CENTER) == BS_CENTER;

	RECT rcClient{};
	::GetClientRect(hWnd, &rcClient);

	rcClient.bottom -= 1;

	RECT rcText{ rcClient };
	RECT rcBackground{ rcClient };
	if (!buffer.empty())
	{
		SIZE szText{};
		::GetTextExtentPoint32W(hdc, buffer.c_str(), static_cast<int>(bufferLen), &szText);

		const int centerPosX = isCenter ? ((rcClient.right - rcClient.left - szText.cx) / 2) : 7;

		rcBackground.top += szText.cy / 2;
		rcText.left += centerPosX;
		rcText.bottom = rcText.top + szText.cy;
		rcText.right = rcText.left + szText.cx + 4;

		::ExcludeClipRect(hdc, rcText.left, rcText.top, rcText.right, rcText.bottom);
	}
	else // There is no text, use "M" to get metrics to move top edge down
	{
		SIZE szText{};
		::GetTextExtentPoint32W(hdc, L"M", 1, &szText);
		rcBackground.top += szText.cy / 2;
	}

	RECT rcContent = rcBackground;
	::GetThemeBackgroundContentRect(hTheme, hdc, BP_GROUPBOX, iStateID, &rcBackground, &rcContent);
	::ExcludeClipRect(hdc, rcContent.left, rcContent.top, rcContent.right, rcContent.bottom);

	dmlib_paint::paintFrameRect(hdc, rcBackground, dmlib::getEdgePen()); // main frame

	::SelectClipRgn(hdc, nullptr);

	// Text part

	if (!buffer.empty())
	{
		::InflateRect(&rcText, -2, 0);

		DTTOPTS dtto{};
		dtto.dwSize = sizeof(DTTOPTS);
		dtto.dwFlags = DTT_TEXTCOLOR;
		dtto.crText = isDisabled ? dmlib::getDisabledTextColor() : dmlib::getTextColor();

		DWORD dtFlags = isCenter ? DT_CENTER : DT_LEFT;

		if (::SendMessage(hWnd, WM_QUERYUISTATE, 0, 0) != 0) // NULL
		{
			dtFlags |= DT_HIDEPREFIX;
		}

		::DrawThemeTextEx(hTheme, hdc, BP_GROUPBOX, iStateID, buffer.c_str(), -1, dtFlags | DT_SINGLELINE, &rcText, &dtto);
	}
}

 

And here is my partially working script:

#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


; group constants
Global Const $BP_GROUPBOX = 4

Global Const $GBS_NORMAL = 1
Global Const $GBS_DISABLED = 2

Example()

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

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

    Local $hGroupProc = DllCallbackRegister(__GUIDarkTheme_GroupProc, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")

    Local $idGroup = GUICtrlCreateGroup("Group 1", 30, 60, 140, 100)
    Local $hGroup = GUICtrlGetHandle(-1)
    Local $idRadio1 = GUICtrlCreateRadio("Radio 1", 40, 80)
    Local $hRadio1= GUICtrlGetHandle(-1)
    Local $idRadio2 = GUICtrlCreateRadio("Radio 2", 40, 110)
    Local $hRadio2 = GUICtrlGetHandle(-1)
    GUICtrlCreateGroup("", -99, -99, 1, 1) ;close group
    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
    $iResize = 3
    _WinAPI_CloseThemeData($hTheme)

    ; subclass group
    _WinAPI_SetWindowSubclass($hGroup, DllCallbackGetPtr($hGroupProc), $idGroup)

    Local $iCount = 1
    ; Subclass controls
 
    $iCount += 1

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

    $iCount += 1

    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($hRadio1, DllCallbackGetPtr($hSubclass), $idRadio1)
    _WinAPI_RemoveWindowSubclass($hRadio2, DllCallbackGetPtr($hSubclass), $idRadio2)
    _WinAPI_RemoveWindowSubclass($hGroup, DllCallbackGetPtr($hGroupProc), $idGroup)
    DllCallbackFree($hSubclass)

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

Func __GUIDarkTheme_GroupProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)

    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1 ; Prevent background erasing to avoid flickering

        Case $WM_PAINT
            ;Return __WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
            Local $tPaint = DllStructCreate($tagPAINTSTRUCT)
            Local $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)

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

            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)

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

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

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

            Local $tText = _WinAPI_GetTextExtentPoint32($hMemDC, $sText)

            Local $rcText = _WinAPI_GetClientRect($hWnd)
            Local $rcBackground = _WinAPI_GetClientRect($hWnd)

            $rcBackground.top += $tText.y / 2
            $rcText.left += 7
            $rcText.bottom = $rcText.top + $tText.y
            $rcText.right = $rcText.left + $tText.x + 4


            _WinAPI_InflateRect($rcText, -2, 0)

            Local $iStateID = 0 ; not sure yet
            Local $tTextRect = _WinAPI_GetThemeBackgroundContentRect($hTheme, $BP_GROUPBOX, $iStateID, $hMemDC, $rcText)
            _WinAPI_ExcludeClipRect($hMemDC, $tTextRect)


            ; Rounded frame (TODO: figure out rounded later)
            Local $hBrush = _WinAPI_CreateSolidBrush(_WinAPI_SwitchColor(0xFFFFFF))
            ;Local $hPen =_WinAPI_CreatePen($PS_SOLID, 1, 0x000000)
            ;_WinAPI_SelectObject($hMemDC, $hPen)
            ;_WinAPI_SelectObject($hMemDC, $hBrush)
            ;_WinAPI_RoundRect($hMemDC, $tClient, 8, 8)
            _WinAPI_FrameRect($hMemDC, $rcBackground, $hBrush)

            _WinAPI_DeleteObject($hBrush)

            _WinAPI_SelectClipRgn($hMemDC, 0)

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

            Local $iTextFlags = BitOR($DT_SINGLELINE, $DT_NOCLIP, $DT_LEFT)

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

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

            ; Cleanup

            _WinAPI_EndPaint($hWnd, $tPaint)
            Return 0

    EndSwitch

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

Func __GUIDarkTheme_ButtonProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)

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

 

Edited by WildByDesign
Posted
#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>

Global Const $BP_RADIOBUTTON = 2
Global Const $BP_CHECKBOX = 3
Global Const $BP_GROUPBOX = 4
Global Const $GBS_NORMAL = 1
Global Const $GBS_DISABLED = 2

Example()

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

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

    Local $idGroup = GUICtrlCreateGroup("Group 1", 30, 40, 140, 110)
    Local $hGroup = GUICtrlGetHandle(-1)
    Local $idRadio1 = GUICtrlCreateRadio("Radio 1", 40, 65)
    Local $hRadio1 = GUICtrlGetHandle(-1)
    Local $idRadio2 = GUICtrlCreateRadio("Radio 2", 40, 95)
    Local $hRadio2 = GUICtrlGetHandle(-1)
    GUICtrlCreateGroup("", -99, -99, 1, 1) ; close group

    Local $idButton_Close = GUICtrlCreateButton("Close", 210, 160)
    _WinAPI_SetWindowTheme(GUICtrlGetHandle(-1), "DarkMode_Explorer")

    ; Subclass group
    _WinAPI_SetWindowSubclass($hGroup, DllCallbackGetPtr($hGroupProc), $idGroup)

    ; Subclass controls
    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 $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($hRadio1, DllCallbackGetPtr($hSubclass), $idRadio1)
    _WinAPI_RemoveWindowSubclass($hRadio2, DllCallbackGetPtr($hSubclass), $idRadio2)
    _WinAPI_RemoveWindowSubclass($hGroup, DllCallbackGetPtr($hGroupProc), $idGroup)
    DllCallbackFree($hSubclass)
    DllCallbackFree($hGroupProc)

    GUIDelete($hGUI)
EndFunc   ;==>Example

Func __GUIDarkTheme_GroupProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1 ; Prevent background erasing to avoid flickering

        Case $WM_PAINT
            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)

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

            ; Ensure parent background is drawn cleanly over empty buffer
            _WinAPI_DrawThemeParentBackground($hWnd, $hMemDC, $tClient)

            ; Get text from groupbox
            Local $sText = _WinAPI_GetWindowText($hWnd)
            Local $tTextSize = _WinAPI_GetTextExtentPoint32($hMemDC, $sText)
            Local $iTextWidth = $tTextSize.X
            Local $iTextHeight = $tTextSize.Y

            ; Compute Frame geometry (Border centers vertically across text height)
            Local $tFrameRect = _WinAPI_GetClientRect($hWnd)
            $tFrameRect.Top += Int($iTextHeight / 2)

            ; Compute accurately-padded exclusion rectangle for text cutout
            Local $tClipRect = _WinAPI_CreateRectEx(7, 0, $iTextWidth + 6, $iTextHeight)

            If $sText <> "" Then
                _WinAPI_ExcludeClipRect($hMemDC, $tClipRect)
            EndIf

            ; Draw border frame (Using a subtle dark gray frame for modern Dark mode appearance)
            Local $hBrush = _WinAPI_CreateSolidBrush(_WinAPI_SwitchColor(0x505050))
            _WinAPI_FrameRect($hMemDC, $tFrameRect, $hBrush)
            _WinAPI_DeleteObject($hBrush)

            ; Remove clipping region restriction to render text safely
            _WinAPI_SelectClipRgn($hMemDC, 0)

            ; Draw Groupbox Label text if it exists
            If $sText <> "" Then
                Local $hTheme = _WinAPI_OpenThemeData($hWnd, "DarkMode_Explorer::Button")
                Local $tDTTOPTS = DllStructCreate($tagDTTOPTS)
                DllStructSetData($tDTTOPTS, 'Size', DllStructGetSize($tDTTOPTS))
                DllStructSetData($tDTTOPTS, 'Flags', $DTT_TEXTCOLOR)
                DllStructSetData($tDTTOPTS, 'clrText', 0xFFFFFF) ; Pure white text

                Local $iTextFlags = BitOR($DT_SINGLELINE, $DT_LEFT, $DT_TOP)
                Local $tDrawTextRect = _WinAPI_CreateRectEx(9, 0, $iTextWidth, $iTextHeight)

                _WinAPI_DrawThemeTextEx($hTheme, $BP_GROUPBOX, $GBS_NORMAL, $hMemDC, $sText, $tDrawTextRect, $iTextFlags, $tDTTOPTS)
                _WinAPI_CloseThemeData($hTheme)
            EndIf

            ; Blit memory buffer back to main UI thread Paint DC
            _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hMemDC, 0, 0, $SRCCOPY)

            ; Thorough Device Context Resource Cleanup
            _WinAPI_SelectObject($hMemDC, $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   ;==>__GUIDarkTheme_GroupProc

Func __GUIDarkTheme_ButtonProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1

        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
            Local Const $RBS_UNCHECKEDNORMAL = 1, $RBS_UNCHECKEDHOT = 2, $RBS_UNCHECKEDPRESSED = 3, $RBS_UNCHECKEDDISABLED = 4
            Local Const $RBS_CHECKEDNORMAL = 5, $RBS_CHECKEDHOT = 6, $RBS_CHECKEDPRESSED = 7, $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

            Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
            Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iW, $iH)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

            Local $iState = _GUICtrlButton_GetState($hWnd)

            If Not _WinAPI_IsWindowEnabled($hWnd) Then
                $iStateID = BitAND($iState, $BST_CHECKED) ? $RBS_CHECKEDDISABLED : $RBS_UNCHECKEDDISABLED
            ElseIf BitAND($iState, $BST_PUSHED) Then
                $iStateID = BitAND($iState, $BST_CHECKED) ? $RBS_CHECKEDPRESSED : $RBS_UNCHECKEDPRESSED
            ElseIf BitAND($iState, $BST_HOT) Then
                $iStateID = BitAND($iState, $BST_CHECKED) ? $RBS_CHECKEDHOT : $RBS_UNCHECKEDHOT
            Else
                $iStateID = BitAND($iState, $BST_CHECKED) ? $RBS_CHECKEDNORMAL : $RBS_UNCHECKEDNORMAL
            EndIf

            Local $iTextFlags = BitOR($DT_SINGLELINE, $DT_NOCLIP, $DT_VCENTER, $DT_LEFT)
            If StringInStr($sStyles, "BS_RIGHT") Then $iTextFlags = BitOR($DT_SINGLELINE, $DT_NOCLIP, $DT_VCENTER, $DT_RIGHT)
            If StringInStr($sStyles, "BS_CENTER") Then $iTextFlags = BitOR($DT_SINGLELINE, $DT_NOCLIP, $DT_VCENTER, $DT_CENTER)

            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)

            _WinAPI_DrawThemeParentBackground($hWnd, $hMemDC, $tClient)

            Local $iWidth = $tSIZE.X
            Local $iHeight = $tSIZE.Y
            Local $iClientHeight = $tClient.Bottom - $tClient.Top
            Local $iYOffset = Int(($iClientHeight - $iHeight) / 2)
            Local $tRect = _WinAPI_CreateRectEx(0, $iYOffset, $iWidth, $iHeight)
            _WinAPI_DrawThemeBackground($hTheme, $iPartID, $iStateID, $hMemDC, $tRect)

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

            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 = $tRect.Right + 4

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

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

            _WinAPI_CloseThemeData($hTheme)
            _WinAPI_SelectObject($hMemDC, $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   ;==>__GUIDarkTheme_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

image.png.6ad048c25958d66a8a091d1117a5ec51.png

 

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

  • Solution
Posted
Spoiler
#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>
#include <GDIPlus.au3>

Global Const $BP_RADIOBUTTON = 2
Global Const $BP_CHECKBOX = 3
Global Const $BP_GROUPBOX = 4
Global Const $GBS_NORMAL = 1
Global Const $GBS_DISABLED = 2

Example()

Func Example()
    _GDIPlus_Startup()

    Local $hGUI = GUICreate("Example", 300, 200)
    GUISetBkColor("0x202020")

    Local $hSubclass = DllCallbackRegister(__GUIDarkTheme_ButtonProc, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    Local $hGroupProc = DllCallbackRegister(__GUIDarkTheme_GroupProc, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")

    ; Standard default styles are preserved, but comctl32 subclass will safely separate background layers.
    Local $idGroup = GUICtrlCreateGroup("Group 1", 30, 40, 140, 110)
    Local $hGroup = GUICtrlGetHandle(-1)

    Local $idRadio1 = GUICtrlCreateRadio("Radio 1", 40, 65)
    Local $hRadio1= GUICtrlGetHandle(-1)
    Local $idRadio2 = GUICtrlCreateRadio("Radio 2", 40, 95)
    Local $hRadio2 = GUICtrlGetHandle(-1)
    GUICtrlCreateGroup("", -99, -99, 1, 1)

    Local $idButton_Close = GUICtrlCreateButton("Close", 210, 160)
    _WinAPI_SetWindowTheme(GUICtrlGetHandle(-1), "DarkMode_Explorer")

    _WinAPI_SetWindowSubclass($hGroup, DllCallbackGetPtr($hGroupProc), $idGroup)

    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 $tStruct3 = DllStructCreate("struct;int;wchar[256];endstruct")
    DllStructSetData($tStruct3, 1, $BP_RADIOBUTTON)
    DllStructSetData($tStruct3, 2, $sStyles)
    _WinAPI_SetWindowSubclass($hRadio2, DllCallbackGetPtr($hSubclass), $idRadio2, DllStructGetPtr($tStruct3))

    GUISetState(@SW_SHOW, $hGUI)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idButton_Close
                ExitLoop
        EndSwitch
    WEnd

    _WinAPI_RemoveWindowSubclass($hRadio1, DllCallbackGetPtr($hSubclass), $idRadio1)
    _WinAPI_RemoveWindowSubclass($hRadio2, DllCallbackGetPtr($hSubclass), $idRadio2)
    _WinAPI_RemoveWindowSubclass($hGroup, DllCallbackGetPtr($hGroupProc), $idGroup)
    DllCallbackFree($hSubclass)
    DllCallbackFree($hGroupProc)

    GUIDelete($hGUI)
    _GDIPlus_Shutdown()
EndFunc   ;==>Example

Func __GUIDarkTheme_GroupProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1

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

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

            Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
            Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iW, $iH)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

            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_DrawThemeParentBackground($hWnd, $hMemDC, $tClient)

            Local $sText = _WinAPI_GetWindowText($hWnd)
            Local $tTextSize = _WinAPI_GetTextExtentPoint32($hMemDC, $sText)
            Local $iTextWidth = $tTextSize.X
            Local $iTextHeight = $tTextSize.Y

            Local $hGraphics = _GDIPlus_GraphicsCreateFromHDC($hMemDC)
            _GDIPlus_GraphicsSetSmoothingMode($hGraphics, 2)

            Local $iRadius = 4
            Local $iX = 1
            Local $iY = Int($iTextHeight / 2)
            Local $iWidth = $iW - 1 - $iX
            Local $iHeight = $iH - 1 - $iY

            Local $hPath = _GDIPlus_PathCreate()
            _GDIPlus_PathAddLine($hPath, $iX + $iRadius, $iY, $iX + $iWidth - $iRadius, $iY)
            _GDIPlus_PathAddArc($hPath, $iX + $iWidth - ($iRadius * 2), $iY, $iRadius * 2, $iRadius * 2, 270, 90)
            _GDIPlus_PathAddLine($hPath, $iX + $iWidth, $iY + $iRadius, $iX + $iWidth, $iY + $iHeight - $iRadius)
            _GDIPlus_PathAddArc($hPath, $iX + $iWidth - ($iRadius * 2), $iY + $iHeight - ($iRadius * 2), $iRadius * 2, $iRadius * 2, 0, 90)
            _GDIPlus_PathAddLine($hPath, $iX + $iWidth - $iRadius, $iY + $iHeight, $iX + $iRadius, $iY + $iHeight)
            _GDIPlus_PathAddArc($hPath, $iX, $iY + $iHeight - ($iRadius * 2), $iRadius * 2, $iRadius * 2, 90, 90)
            _GDIPlus_PathAddLine($hPath, $iX, $iY + $iHeight - $iRadius, $iX, $iY + $iRadius)
            _GDIPlus_PathAddArc($hPath, $iX, $iY, $iRadius * 2, $iRadius * 2, 180, 90)
            _GDIPlus_PathCloseFigure($hPath)

            If $sText <> "" Then
                Local $hRegion = _GDIPlus_RegionCreate()
                _GDIPlus_RegionCombineRect($hRegion, 9, 0, $iTextWidth + 4, $iTextHeight, 0)
                _GDIPlus_GraphicsSetClipRegion($hGraphics, $hRegion, 3)
                _GDIPlus_RegionDispose($hRegion)
            EndIf

            Local $hPen = _GDIPlus_PenCreate(0xFF505050, 1)
            _GDIPlus_GraphicsDrawPath($hGraphics, $hPath, $hPen)

            _GDIPlus_GraphicsResetClip($hGraphics)
            _GDIPlus_PenDispose($hPen)
            _GDIPlus_PathDispose($hPath)

            If $sText <> "" Then
                Local $hTheme = _WinAPI_OpenThemeData($hWnd, "DarkMode_Explorer::Button")
                Local $tDTTOPTS = DllStructCreate($tagDTTOPTS)
                DllStructSetData($tDTTOPTS, 'Size', DllStructGetSize($tDTTOPTS))
                DllStructSetData($tDTTOPTS, 'Flags', $DTT_TEXTCOLOR)
                DllStructSetData($tDTTOPTS, 'clrText', 0xFFFFFF)

                Local $iTextFlags = BitOR($DT_SINGLELINE, $DT_LEFT, $DT_TOP)
                Local $tDrawTextRect = _WinAPI_CreateRectEx(11, 0, $iTextWidth, $iTextHeight)

                _WinAPI_DrawThemeTextEx($hTheme, $BP_GROUPBOX, $GBS_NORMAL, $hMemDC, $sText, $tDrawTextRect, $iTextFlags, $tDTTOPTS)
                _WinAPI_CloseThemeData($hTheme)
            EndIf

            _GDIPlus_GraphicsDispose($hGraphics)

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

            _WinAPI_SelectObject($hMemDC, $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   ;==>__GUIDarkTheme_GroupProc

Func __GUIDarkTheme_ButtonProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1

        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
            Local Const $RBS_UNCHECKEDNORMAL = 1, $RBS_UNCHECKEDHOT = 2, $RBS_UNCHECKEDPRESSED = 3, $RBS_UNCHECKEDDISABLED = 4
            Local Const $RBS_CHECKEDNORMAL = 5, $RBS_CHECKEDHOT = 6, $RBS_CHECKEDPRESSED = 7, $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

            Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
            Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iW, $iH)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

            Local $iState = _GUICtrlButton_GetState($hWnd)

            If Not _WinAPI_IsWindowEnabled($hWnd) Then
                $iStateID = BitAND($iState, $BST_CHECKED) ? $RBS_CHECKEDDISABLED : $RBS_UNCHECKEDDISABLED
            ElseIf BitAND($iState, $BST_PUSHED) Then
                $iStateID = BitAND($iState, $BST_CHECKED) ? $RBS_CHECKEDPRESSED : $RBS_UNCHECKEDPRESSED
            ElseIf BitAND($iState, $BST_HOT) Then
                $iStateID = BitAND($iState, $BST_CHECKED) ? $RBS_CHECKEDHOT : $RBS_UNCHECKEDHOT
            Else
                $iStateID = BitAND($iState, $BST_CHECKED) ? $RBS_CHECKEDNORMAL : $RBS_UNCHECKEDNORMAL
            EndIf

            Local $iTextFlags = BitOR($DT_SINGLELINE, $DT_NOCLIP, $DT_VCENTER, $DT_LEFT)
            If StringInStr($sStyles, "BS_RIGHT") Then $iTextFlags = BitOR($DT_SINGLELINE, $DT_NOCLIP, $DT_VCENTER, $DT_RIGHT)
            If StringInStr($sStyles, "BS_CENTER") Then $iTextFlags = BitOR($DT_SINGLELINE, $DT_NOCLIP, $DT_VCENTER, $DT_CENTER)

            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)

            _WinAPI_DrawThemeParentBackground($hWnd, $hMemDC, $tClient)

            Local $iWidth = $tSIZE.X
            Local $iHeight = $tSIZE.Y
            Local $iClientHeight = $tClient.Bottom - $tClient.Top
            Local $iYOffset = Int(($iClientHeight - $iHeight) / 2)
            Local $tRect = _WinAPI_CreateRectEx(0, $iYOffset, $iWidth, $iHeight)
            _WinAPI_DrawThemeBackground($hTheme, $iPartID, $iStateID, $hMemDC, $tRect)

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

            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 = $tRect.Right + 4

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

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

            _WinAPI_CloseThemeData($hTheme)
            _WinAPI_SelectObject($hMemDC, $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   ;==>__GUIDarkTheme_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

..rounded corners
   image.png.93244a07bb7dea54e4a37fa6eaf5f7ab.png

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

Posted

@argumentum This is so impressive. I go to sleep and you do some sort of magic overnight. Excellent work!

Everything lines up perfectly. We've got a squared version which I suppose could work for Windows 10 users and a rounded version for Windows 11 users. And with the amount of Groupboxes that we have coming up soon, that would have been blinding for the users on those affected OS builds. You are going to save many eyes with this work. Thank you. :)

 

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