Jump to content

Recommended Posts

Posted (edited)

I tried to apply dark mode to the SampleControls.au3 example.

What I did so far:

; Coded by UEZ build 2025-10-17 beta
;not DPI aware!

#include <APIConstants.au3>
#include <AVIConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiDateTimePicker.au3>
#include <GuiMenu.au3>
#include <GuiMonthCal.au3>
#include <GuiScrollBars.au3>
#include <GuiTab.au3>
#include <TreeViewConstants.au3>
#include <ListViewConstants.au3>
#include <WinAPIConstants.au3>
#include <WinAPIGdi.au3>
#include <WinAPIRes.au3>
#include <WinAPIShellEx.au3>
#include <WinAPISys.au3>
#include <WinAPISysWin.au3>
#include <WinAPITheme.au3>
#include <WindowsConstants.au3>

Enum $IHCM_USE_CACHED_VALUE, $IHCM_REFRESH
Enum $APPMODE_DEFAULT = 0, $APPMODE_ALLOWDARK, $APPMODE_FORCEDARK, $APPMODE_FORCELIGHT, $APPMODE_MAX
Const $PRF_CLIENT = 0x04

; Dark Mode Colors (RGB)
Global Const $COLOR_BG_DARK = 0x202020
Global Const $COLOR_TEXT_LIGHT = 0xFFFFFF
Global Const $COLOR_CONTROL_BG = 0x2B2B2B
Global Const $COLOR_EDIT_BG = 0x1E1E1E
Global Const $COLOR_BUTTON_BG = 0x333333
Global Const $COLOR_BORDER = 0x3F3F3F

; Global variables for subclassing (MUST be declared before _Example()!)
Global $g_hGUI = 0, $g_hTab, $g_ListView
Global $g_aControls[50][3] = [[0, 0, 0]] ; [ControlID, hWnd, OldWndProc]
Global $g_iControlCount = 0
Global $g_pSubclassProc = 0

; Global brushes for _WM_CTLCOLOR (avoids memory leaks)
Global $g_hBrushEdit = 0
Global $g_hBrushButton = 0
Global $g_hBrushBg = 0
Global $g_hBrushGreen
Global $g_hLabelGreen = 0, $g_idLabelGreen
Global $g_idLabelPic, $g_hLabelPic
Global $g_hMenu = 0
Global $g_idDate = 0, $g_hDate = 0

; Global variable for tab subclassing
Global $g_hTab_CB, $g_pTab_CB, $g_hProc

; Structure for NM_CUSTOMDRAW notification
Global Const $tagNMCUSTOMDRAW = _
            $tagNMHDR & ";" & _                                                 ; Contains NM_CUSTOMDRAW / NMHDR header among other things
            "dword dwDrawStage;" & _                                            ; Current drawing stage (CDDS_*)
            "handle hdc;" & _                                                   ; Device Context Handle
            "long left;long top;long right;long bottom;" & _                    ; Drawing rectangle
            "dword_ptr dwItemSpec;" & _                                         ; Item index or other info (depending on the control)
            "uint uItemState;" & _                                              ; State Flags (CDIS_SELECTED, CDIS_FOCUS etc.)
            "lparam lItemlParam"                                                ; lParam set by the item (e.g., via LVITEM.lParam)


Global $g_hMenu = 0, $hMenuFont
Global $g_aMenuText = [] ; dynamic array for top-level menu texts
Global $arMenuItems[1][8]
$arMenuItems[0][0] = 0
Global $arSideItems[1][10]
$arSideItems[0][0] = 0

Const $ODT_MENU = 1
Const $ODS_SELECTED = 0x0001
Const $ODS_DISABLED = 0x0004

_Example()

Func _Example()
    ; Create global brushes
    $g_hBrushEdit = _WinAPI_CreateSolidBrush(_ColorToCOLORREF($COLOR_EDIT_BG))
    $g_hBrushButton = _WinAPI_CreateSolidBrush(_ColorToCOLORREF($COLOR_BUTTON_BG))
    $g_hBrushBg = _WinAPI_CreateSolidBrush(_ColorToCOLORREF($COLOR_BG_DARK))

    #Region GUI
    $g_hGUI = GUICreate("Sample GUI with Dark Mode", 400, 400)
    GUISetIcon(@SystemDir & "\mspaint.exe", 0)
    GUISetBkColor($COLOR_BG_DARK, $g_hGUI)

    ; Register GUI-level WM_CTLCOLOR messages
    GUIRegisterMsg($WM_CTLCOLOREDIT,     "_WM_CTLCOLOR")
    GUIRegisterMsg($WM_CTLCOLORLISTBOX,  "_WM_CTLCOLOR")
    GUIRegisterMsg($WM_CTLCOLORBTN,      "_WM_CTLCOLOR")
    GUIRegisterMsg($WM_CTLCOLORSTATIC,   "_WM_CTLCOLOR")
    GUIRegisterMsg($WM_INITMENUPOPUP,    "_OnInitMenuPopup")
    GUIRegisterMsg($WM_MEASUREITEM,      "WM_MEASUREITEM_Handler")
    GUIRegisterMsg($WM_DRAWITEM,         "WM_DRAWITEM_Handler")
    GUIRegisterMsg($WM_NOTIFY,           "WM_NOTIFY")
    GUIRegisterMsg($WM_ACTIVATE,         "_WM_ACTIVATE_OverpaintLine")
    GUIRegisterMsg($WM_WINDOWPOSCHANGED, "_WM_WINDOWPOSCHANGED_OverpaintLine")
    #EndRegion GUI

    #Region MENU
    Global $g_aMenuText[4]
    $g_aMenuText[0] = "Menu &One"
    $g_aMenuText[1] = "Menu &Two"
    $g_aMenuText[2] = "Menu Th&ree"
    $g_aMenuText[3] = "Menu &Four"

    Local $idMenu1 = GUICtrlCreateMenu($g_aMenuText[0])
    Local $idMenu2 = GUICtrlCreateMenu($g_aMenuText[1])
    GUICtrlCreateMenu($g_aMenuText[2])
    GUICtrlCreateMenu($g_aMenuText[3])
    GUICtrlCreateMenuItem('SubMenu One &A', $idMenu1)
    GUICtrlCreateMenuItem('SubMenu One &B', $idMenu1)

    ; Owner-draw Top-Level Menu einrichten
    _MakeMenuOwnerDraw($g_hGUI)

    #EndRegion MENU

    #Region CONTEXT MENU
    Local $idContextMenu = GUICtrlCreateContextMenu()
    GUICtrlCreateMenuItem("Context Menu", $idContextMenu)
    GUICtrlCreateMenuItem("", $idContextMenu)
    GUICtrlCreateMenuItem("&Properties", $idContextMenu)
    #EndRegion CONTEXT MENU

    #Region PIC
    Local $idPic = GUICtrlCreatePic("", 0, 0, 169, 68)
    GUICtrlSetImage($idPic, "C:\Program Files (x86)\AutoIt3\Examples\GUI\logo4.gif")
    GUICtrlSetTip(-1, '#Region PIC')
    $g_idLabelPic = GUICtrlCreateLabel("Sample Pic", 75, 1, 53, 15)
    $g_hLabelPic = GUICtrlGetHandle($g_idLabelPic)
    #EndRegion PIC

    #Region AVI
    Local $idAvi = GUICtrlCreateAvi("C:\Program Files (x86)\AutoIt3\Examples\GUI\SampleAVI.avi", 0, 180, 10, 32, 32, $ACS_AUTOPLAY)
    GUICtrlSetTip(-1, '#Region AVI')
    GUICtrlCreateLabel("Sample avi", 175, 50)
    #EndRegion AVI

    #Region TAB
    Local $idTab = GUICtrlCreateTab(240, 0, 150, 70), $g_hTab = GUICtrlGetHandle($idTab)
    _AddControlForSubclass($idTab)
    GUICtrlCreateTabItem("One")
    GUICtrlSetTip(-1, '#Region TAB1')
    GUICtrlCreateLabel("Sample Tab", 250, 40)
    GUICtrlCreateTabItem("Two")
    GUICtrlSetTip(-1, '#Region TAB2')
    GUICtrlCreateTabItem("Three")
    GUICtrlSetTip(-1, '#Region TAB3')
    GUICtrlCreateTabItem("")
    $g_hTab = GUICtrlGetHandle($idTab)
    #EndRegion TAB

    #Region COMBO
    Local $idCombo = GUICtrlCreateCombo("Sample Combo", 250, 80, 120, 100)
    GUICtrlSetData($idCombo, "Item 2|Item 3", "Sample Combo")
    _AddControlForSubclass($idCombo)
    GUICtrlSetTip(-1, '#Region COMBO')
    #EndRegion COMBO

    #Region PROGRESS
    Local $idProgress = GUICtrlCreateProgress(60, 80, 150, 20)
    _AddControlForSubclass($idProgress)
    GUICtrlSetTip(-1, '#Region PROGRESS')
    GUICtrlSetData(-1, 60)
    GUICtrlCreateLabel("Progress:", 5, 82)
    #EndRegion PROGRESS

    #Region EDIT
    Local $idEdit = GUICtrlCreateEdit(@CRLF & "  Sample Edit Control", 10, 110, 150, 70)
    _AddControlForSubclass($idEdit)
    GUICtrlSetTip(-1, '#Region EDIT')
    #EndRegion EDIT

    #Region LIST
    Local $idList = GUICtrlCreateList("", 5, 190, 100, 90)
    _AddControlForSubclass($idList)
    GUICtrlSetTip(-1, '#Region LIST')
    GUICtrlSetData(-1, "A.Sample|B.List|C.Control|D.Here", "B.List")
    #EndRegion LIST

    #Region ICON
    GUICtrlCreateIcon("explorer.exe", 0, 175, 120)
    GUICtrlSetTip(-1, '#Region ICON')
    GUICtrlCreateLabel("Icon", 180, 160, 50, 20)
    #EndRegion ICON

    #Region LIST VIEW
    Local $idListView = GUICtrlCreateListView("Sample|ListView|", 110, 190, 110, 80, $LVS_REPORT)
    _AddControlForSubclass($idListView)
    GUICtrlSetBkColor($idListView, $COLOR_EDIT_BG)
    GUICtrlSetColor($idListView, $COLOR_TEXT_LIGHT)
    GUICtrlSetTip(-1, '#Region LIST VIEW')
    GUICtrlCreateListViewItem("A|One", $idListView)
    GUICtrlCreateListViewItem("B|Two", $idListView)
    GUICtrlCreateListViewItem("C|Three", $idListView)
    $g_ListView = GUICtrlGetHandle($idListView)
    #EndRegion LIST VIEW

    #Region GROUP WITH RADIO BUTTONS
    Local $idGroup = GUICtrlCreateGroup("Sample Group", 230, 120)
    GUICtrlSetColor($idGroup, $COLOR_TEXT_LIGHT)
    Local $idRadio1 = GUICtrlCreateRadio("Radio One", 250, 140, 80)
    GUICtrlSetTip($idRadio1, '#Region RADIO1')
    GUICtrlSetState($idRadio1, $GUI_CHECKED)
    Local $idRadio2 = GUICtrlCreateRadio("Radio Two", 250, 165, 80)
    GUICtrlSetTip($idRadio2, '#Region RADIO2')
    GUICtrlCreateGroup("", -99, -99, 1, 1)
    DllCall("UxTheme.dll", "int", "SetWindowTheme", "hwnd", GUICtrlGetHandle($idGroup), "wstr", 0, "wstr", 0)
    DllCall("UxTheme.dll", "int", "SetWindowTheme", "hwnd", GUICtrlGetHandle($idRadio1), "wstr", 0, "wstr", 0)
    DllCall("UxTheme.dll", "int", "SetWindowTheme", "hwnd", GUICtrlGetHandle($idRadio2), "wstr", 0, "wstr", 0)
    #EndRegion GROUP WITH RADIO BUTTONS

    #Region UPDOWN
    GUICtrlCreateLabel("UpDown", 350, 115)
    GUICtrlSetColor(-1, $COLOR_TEXT_LIGHT)
    Local $idInput = GUICtrlCreateInput("42", 350, 130, 40, 20)
    _AddControlForSubclass($idInput)
    Local $idUpDown = GUICtrlCreateUpdown(-1)
    _AddControlForSubclass($idUpDown)
    #EndRegion UPDOWN

    #Region LABEL
    $g_idLabelGreen = GUICtrlCreateLabel("Green" & @CRLF & "Label", 350, 165, 40, 40)
    $g_hLabelGreen = GUICtrlGetHandle($g_idLabelGreen)
    GUICtrlSetTip($g_idLabelGreen, '#Region LABEL')
    $g_hBrushGreen = _WinAPI_CreateSolidBrush(_ColorToCOLORREF(0x00FF00)) ; green background

    #Region SLIDER
    GUICtrlCreateLabel("Slider:", 235, 215)
    Local $idSlider = GUICtrlCreateSlider(270, 210, 120, 30)
    _AddControlForSubclass($idSlider)
    GUICtrlSetTip(-1, '#Region SLIDER')
    GUICtrlSetData(-1, 30)
    #EndRegion SLIDER

    #Region INPUT
    Local $idInput2 = GUICtrlCreateInput("Sample Input Box", 235, 255, 130, 20)
    _AddControlForSubclass($idInput2)
    GUICtrlSetTip(-1, '#Region INPUT')
    #EndRegion INPUT

    #Region DATE
    $g_idDate = GUICtrlCreateDate("", 5, 280, 200, 20)
    $g_hDate = GUICtrlGetHandle($g_idDate)
    _AddControlForSubclass($g_idDate)
    GUICtrlSetTip(-1, '#Region DATE')
    GUICtrlCreateLabel("(Date control expands into a calendar)", 10, 305, 200, 20)
    #EndRegion DATE

    #Region BUTTON
    Local $idButton = GUICtrlCreateButton("Sample Button", 10, 330, 100, 30)
    _AddControlForSubclass($idButton)
    GUICtrlSetTip(-1, '#Region BUTTON')
    #EndRegion BUTTON

    #Region CHECKBOX
    Local $idCheckBox = GUICtrlCreateCheckbox("Checkbox", 130, 335, 80, 20)
    GUICtrlSetTip(-1, '#Region CHECKBOX')
    DllCall("UxTheme.dll", "int", "SetWindowTheme", "hwnd", GUICtrlGetHandle($idCheckBox), "wstr", 0, "wstr", 0)
    #EndRegion CHECKBOX

    #Region TREEVIEW ONE
    Local $idTreeView1 = GUICtrlCreateTreeView(210, 290, 80, 80)
    _AddControlForSubclass($idTreeView1)
    GUICtrlSetBkColor($idTreeView1, $COLOR_EDIT_BG)
    GUICtrlSetColor($idTreeView1, $COLOR_TEXT_LIGHT)
    GUICtrlSetTip(-1, '#Region TREEVIEW ONE')
    Local $idTreeViewItem = GUICtrlCreateTreeViewItem("TreeView", $idTreeView1)
    GUICtrlCreateTreeViewItem("Item1", $idTreeViewItem)
    GUICtrlCreateTreeViewItem("Item2", $idTreeViewItem)
    GUICtrlCreateTreeViewItem("Foo", -1)
    GUICtrlSetState($idTreeViewItem, $GUI_EXPAND)
    #EndRegion TREEVIEW ONE

    #Region TREEVIEW TWO
    Local $idTreeView2 = GUICtrlCreateTreeView(295, 290, 103, 80, $TVS_CHECKBOXES)
    _AddControlForSubclass($idTreeView2)
    GUICtrlSetBkColor($idTreeView2, $COLOR_EDIT_BG)
    GUICtrlSetColor($idTreeView2, $COLOR_TEXT_LIGHT)
    GUICtrlSetTip(-1, '#Region TREEVIEW TWO')
    GUICtrlCreateTreeViewItem("TreeView", $idTreeView2)
    GUICtrlCreateTreeViewItem("With", $idTreeView2)
    GUICtrlCreateTreeViewItem("$TVS_CHECKBOXES", $idTreeView2)
    GUICtrlSetState(-1, $GUI_CHECKED)
    GUICtrlCreateTreeViewItem("Style", $idTreeView2)
    #EndRegion TREEVIEW TWO

    ; Apply Dark Mode
    _ApplyDarkModeToAllControls()

    ; Handle scrollbars for windows that have them
    _EnableDarkScrollBars()

    ; Register a custom window procedure for the tab control for owner-drawing
    $g_hTab_CB = DllCallbackRegister('_WinProc', 'ptr', 'hwnd;uint;wparam;lparam')
    $g_pTab_CB = DllCallbackGetPtr($g_hTab_CB)
    $g_hProc = _WinAPI_SetWindowLong($g_hTab, $GWL_WNDPROC, $g_pTab_CB)

    GUISetState(@SW_SHOW)

    _OverpaintWhiteLine()
    _WinAPI_RedrawWindow($g_hGUI, 0, 0, BitOR($RDW_INVALIDATE, $RDW_UPDATENOW))

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                _CleanupSubclassing()
                _CleanupBrushes()
                ExitLoop
        EndSwitch
    WEnd
    ; Restore the original window procedure for the tab control
    _WinAPI_SetWindowLong($g_hTab, $GWL_WNDPROC, $g_hProc)
    DllCallbackFree($g_hTab_CB)

    GUIDelete()
EndFunc   ;==>_Example

Func _OverpaintWhiteLine()
    Local $hDC = _WinAPI_GetWindowDC($g_hGUI)
    If Not $hDC Then Return

    Local $tWndRect = _WinAPI_GetWindowRect($g_hGUI)
    Local $iWndWidth = $tWndRect.right - $tWndRect.left

    ; 1. Caption height
    Local $iCaptionHeight = _WinAPI_GetSystemMetrics($SM_CYCAPTION)

    ; 2. Border height (top)
    Local $iBorderHeight = _WinAPI_GetSystemMetrics($SM_CYSIZEFRAME)
    If $iBorderHeight = 0 Then $iBorderHeight = _WinAPI_GetSystemMetrics($SM_CYFIXEDFRAME)

    ; 3. Determine menu height dynamically
    Local $iMenuHeight = _WinAPI_GetSystemMetrics($SM_CYMENU) ; standard menu height

    ; Alternative: get menu height via GetMenuBarInfo
    Local $tMenuBarInfo = DllStructCreate("dword cbSize;long left;long top;long right;long bottom;handle hwndMenu;handle hwndItem;bool fBarFocused;bool fFocused")
    DllStructSetData($tMenuBarInfo, "cbSize", DllStructGetSize($tMenuBarInfo))
    Local $aResult = DllCall("user32.dll", "bool", "GetMenuBarInfo", "hwnd", $g_hGUI, "long", 0xFFFFFFFD, "long", 0, "ptr", DllStructGetPtr($tMenuBarInfo))

    If IsArray($aResult) And $aResult[0] Then
        ; Calculate the actual menu height from the coordinates
        Local $iMenuTop = $tMenuBarInfo.top
        Local $iMenuBottom = $tMenuBarInfo.bottom
        ; Convert to window coordinates
        $iMenuHeight = ($iMenuBottom - $iMenuTop)
    EndIf

    ; The white line is directly below the menu
    Local $iWhiteLineY = $iCaptionHeight + $iBorderHeight + $iMenuHeight - _WinAPI_GetSystemMetrics($SM_CYFIXEDFRAME) * 2

;~     ConsoleWrite("Caption: " & $iCaptionHeight & ", Border: " & $iBorderHeight & ", Menu: " & $iMenuHeight & " -> White Line at Y=" & $iWhiteLineY & @CRLF)

    ; Overpaint the white line (1x2 pixels)
    Local $tRect = DllStructCreate($tagRECT)
    With $tRect
        .left = 0
        .top = $iWhiteLineY
        .right = $iWndWidth
        .bottom = $iWhiteLineY + 2 ; 2 pixels high
    EndWith

    Local $hBrush = _WinAPI_CreateSolidBrush(_ColorToCOLORREF($COLOR_BG_DARK))
;~     Local $hBrush = _WinAPI_CreateSolidBrush(_ColorToCOLORREF(0xFF0000))
    _WinAPI_FillRect($hDC, $tRect, $hBrush)
    _WinAPI_DeleteObject($hBrush)
    _WinAPI_ReleaseDC($g_hGUI, $hDC)
EndFunc   ;==>_OverpaintWhiteLine

Func _WM_ACTIVATE_OverpaintLine($hWnd, $iMsg, $wParam, $lParam)
    If $hWnd <> $g_hGUI Then Return $GUI_RUNDEFMSG
    _OverpaintWhiteLine()
    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_ACTIVATE_OverpaintLine

Func _WM_WINDOWPOSCHANGED_OverpaintLine($hWnd, $iMsg, $wParam, $lParam)
    If $hWnd <> $g_hGUI Then Return $GUI_RUNDEFMSG
    _OverpaintWhiteLine()
    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_WINDOWPOSCHANGED_OverpaintLine

Func _OnInitMenuPopup($hWnd, $iMsg, $wParam, $lParam)
    ; wParam = HMENU of the popup, lParam = position/index - not needed here
    ; A small delay sometimes helps to ensure the popup window exists
    Sleep(100)

    ; The foreground window is most likely the new menu popup
    Local $hPopup = _WinAPI_GetForegroundWindow()
    If Not $hPopup Then Return $GUI_RUNDEFMSG

    Local $sCls = StringLower(_WinAPI_GetClassName($hPopup))
    If $sCls <> "#32768" And $sCls <> "popupmenu" Then
        ; if no menu popup is detected -> do nothing
        Return $GUI_RUNDEFMSG
    EndIf

    ; Set Theme + AllowDarkMode on the popup itself
    _WinAPI_SetWindowTheme($hPopup, "DarkMode_Explorer", "")
    _WinAPI_AllowDarkModeForWindow($hPopup, True)

    ; Also apply the theme to all child windows of the popup (e.g., scrollbars)
    Local $hChild = _WinAPI_GetWindow($hPopup, $GW_CHILD)
    While $hChild
        Local $sChildCls = StringLower(_WinAPI_GetClassName($hChild))
        ; apply theme specifically for scrollbars, UpDown, etc.
        If $sChildCls = "scrollbar" Or $sChildCls = "msctls_updown32" Or $sChildCls = "traynotifywnd" Then
            _WinAPI_SetWindowTheme($hChild, "DarkMode_Explorer", "")
            _WinAPI_AllowDarkModeForWindow($hChild, True)
        Else
            ; try generically
            _WinAPI_SetWindowTheme($hChild, "DarkMode_Explorer", "")
            _WinAPI_AllowDarkModeForWindow($hChild, True)
        EndIf
        $hChild = _WinAPI_GetWindow($hChild, $GW_HWNDNEXT)
    WEnd

    ; Force refresh so the change is visible immediately
    _WinAPI_FlushMenuThemes()
    _WinAPI_RefreshImmersiveColorPolicyState()
    _WinAPI_RedrawWindow($hPopup, 0, 0, BitOR($RDW_INVALIDATE, $RDW_UPDATENOW))

    Return $GUI_RUNDEFMSG
EndFunc   ;==>_OnInitMenuPopup

Func _ColorToCOLORREF($iColor) ;RGB to BGR
    Local $iR = BitAND(BitShift($iColor, 16), 0xFF)
    Local $iG = BitAND(BitShift($iColor, 8), 0xFF)
    Local $iB = BitAND($iColor, 0xFF)
    Return BitOR(BitShift($iB, -16), BitShift($iG, -8), $iR)
EndFunc   ;==>_ColorToCOLORREF

Func _AddControlForSubclass($iCtrlID)
    Local $hCtrl = GUICtrlGetHandle($iCtrlID)
    If $hCtrl Then
        $g_aControls[$g_iControlCount][0] = $iCtrlID
        $g_aControls[$g_iControlCount][1] = $hCtrl
        $g_aControls[$g_iControlCount][2] = 0 ; Placeholder for OldWndProc
        $g_iControlCount += 1
    EndIf
EndFunc   ;==>_AddControlForSubclass

Func _ApplyDarkModeToAllControls()
    ; DWM Dark Mode for the main window
    _WinAPI_SetPreferredAppMode($APPMODE_FORCEDARK)

    ; Create subclass callback
    If Not $g_pSubclassProc Then
        $g_pSubclassProc = DllCallbackRegister("_SubclassProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    EndIf

    ; Subclass all controls
    For $i = 0 To $g_iControlCount - 1
        Local $hCtrl = $g_aControls[$i][1]
        If $hCtrl Then
            Local $sClass = _WinAPI_GetClassName($hCtrl)
            ; Use SetWindowSubclass
            _WinAPI_SetWindowSubclass($hCtrl, DllCallbackGetPtr($g_pSubclassProc), $i, 0)

            ; Special themes for different control types
            Switch StringLower($sClass)
                Case "edit", "richedit", "richedit20a", "richedit20w"
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_CFD", 0)
                Case "button"
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_Explorer", 0)
                Case "combobox"
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_CFD", 0)
                    ; Handle ComboBox child-edit
                    Local $hEdit = _WinAPI_FindWindowEx($hCtrl, 0, "Edit", "")
                    If $hEdit Then
                        _WinAPI_SetWindowTheme($hEdit, "DarkMode_CFD", 0)
                        _WinAPI_AllowDarkModeForWindow($hEdit, True)
                    EndIf
                    ; ComboBox dropdown list
                    Local $hComboLBox = _WinAPI_FindWindowEx($hCtrl, 0, "ComboLBox", "")
                    If $hComboLBox Then
                        _WinAPI_SetWindowTheme($hComboLBox, "DarkMode_Explorer", 0)
                        _WinAPI_AllowDarkModeForWindow($hComboLBox, True)
                    EndIf
                Case "syslistview32"
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_Explorer", 0)
                    ; ListView extended styles for better dark mode
                    _SendMessage($hCtrl, $LVS_EX_DOUBLEBUFFER, 0x00010000, 0x00010000)
                    ; Also make the ListView header dark
                    Local $hHeader = _SendMessage($hCtrl, $LVM_GETHEADER, 0, 0)
                    If $hHeader Then
                        _WinAPI_SetWindowTheme($hHeader, "DarkMode_ItemsView", 0)
                    EndIf
                Case "systreeview32"
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_Explorer", 0)
                Case "msctls_trackbar32" ; Slider
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_Explorer", 0)
                Case "systabcontrol32"
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_Explorer", "") ;must be ""
                    ; tab-Control background
                    _SendMessage($hCtrl, 0x132D, 0, $COLOR_BG_DARK) ; TCM_SETBKCOLOR
                    ; Try to make the UpDown (spinner for too many tabs) dark as well
                    Local $hUpDown = _WinAPI_FindWindowEx($hCtrl, 0, "msctls_updown32", "")
                    If $hUpDown Then _WinAPI_SetWindowTheme($hUpDown, "DarkMode_Explorer", 0)
                Case "listbox"
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_Explorer", 0)
                Case "msctls_progress32"
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_Explorer", 0)
                Case "scrollbar"
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_Explorer", 0)
                Case Else
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_Explorer", 0)
            EndSwitch

            _WinAPI_AllowDarkModeForWindow($hCtrl, True)
        EndIf
    Next

    ; Update theme system
    _WinAPI_RefreshImmersiveColorPolicyState()
    _WinAPI_FlushMenuThemes()
    _WinAPI_DwmSetWindowAttribute($g_hGUI, $DWMWA_USE_IMMERSIVE_DARK_MODE, True)


    ; Redraw GUI
    _WinAPI_RedrawWindow($g_hGUI, 0, 0, $RDW_UPDATENOW)
EndFunc   ;==>_ApplyDarkModeToAllControls

Func _CleanupSubclassing()
    ; Remove all subclasses
    If $g_pSubclassProc Then
        For $i = 0 To $g_iControlCount - 1
            Local $hCtrl = $g_aControls[$i][1]
            If $hCtrl Then
                _WinAPI_RemoveWindowSubclass($hCtrl, DllCallbackGetPtr($g_pSubclassProc), $i)
            EndIf
        Next
        DllCallbackFree($g_pSubclassProc)
        $g_pSubclassProc = 0
    EndIf
EndFunc   ;==>_CleanupSubclassing

Func _SubclassProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Switch $iMsg
        Case $WM_NOTIFY
            Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
            Local $hFrom = $tNMHDR.hWndFrom
            Local $iCode = $tNMHDR.Code

            ; --- Adjust ListView Header text color ---
            If $iCode = $NM_CUSTOMDRAW Then
                Local $tNMCUSTOMDRAW = DllStructCreate($tagNMCUSTOMDRAW, $lParam)
                Local $dwDrawStage = $tNMCUSTOMDRAW.dwDrawStage
                Local $hDC = $tNMCUSTOMDRAW.hdc

                Switch $dwDrawStage
                    Case $CDDS_PREPAINT
                        Return $CDRF_NOTIFYITEMDRAW
                    Case $CDDS_ITEMPREPAINT
                        _WinAPI_SetTextColor($hDC, _ColorToCOLORREF($COLOR_TEXT_LIGHT)) ; White text
                        _WinAPI_SetBkColor($hDC, _ColorToCOLORREF($COLOR_BG_DARK))     ; Dark background
                        Return BitOR($CDRF_NEWFONT, $CDRF_NOTIFYPOSTPAINT)
                EndSwitch
            EndIf

        Case $WM_PAINT
            ; Custom Paint for better Dark Mode rendering
            Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
    EndSwitch

    ; Forward standard message to DefSubclassProc
    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_SubclassProc

Func _MakeMenuOwnerDraw($hWnd)
    ; Get menu handle
    $g_hMenu = _GUICtrlMenu_GetMenu($hWnd)
    If Not $g_hMenu Then Return False

    Local $iCount = _GUICtrlMenu_GetItemCount($g_hMenu)
    If $iCount <= 0 Then Return False

    ReDim $g_aMenuText[$iCount]

    For $i = 0 To $iCount - 1
        ; retrieve text via GetMenuStringW (works better than _GUICtrlMenu_GetItemText)
        Local $tText = DllStructCreate("wchar s[256]")
        Local $iLen = DllCall("user32.dll", "int", "GetMenuStringW", _
                                                    "handle", $g_hMenu, _
                                                    "uint", $i, _
                                                    "struct*", $tText, _
                                                    "int", 255, _
                                                    "uint", 0x0400) ; MF_BYPOSITION

        If IsArray($iLen) And $iLen[0] > 0 Then
            $g_aMenuText[$i] = $tText.s
        Else
            $g_aMenuText[$i] = ""
        EndIf

        ; set top-level item to owner-draw
        _GUICtrlMenu_SetItemType($g_hMenu, $i, $MFT_OWNERDRAW, True)
    Next

    ; redraw menu bar immediately
    _GUICtrlMenu_DrawMenuBar($hWnd)
    _WinAPI_RedrawWindow($hWnd, 0, 0, BitOR($RDW_INVALIDATE, $RDW_UPDATENOW))

    Return True
EndFunc   ;==>_MakeMenuOwnerDraw

Func WM_MEASUREITEM_Handler($hWnd, $iMsg, $wParam, $lParam)
    Local $tagMEASUREITEM = "uint CtlType;uint CtlID;uint itemID;uint itemWidth;uint itemHeight;ulong_ptr itemData"
    Local $t = DllStructCreate($tagMEASUREITEM, $lParam)
    If Not IsDllStruct($t) Then Return $GUI_RUNDEFMSG

    If $t.CtlType <> $ODT_MENU Then Return $GUI_RUNDEFMSG

    Local $itemID = $t.itemID

    ; itemID is the control ID, not the position!
    ; We must derive the position from the itemID
    Local $iPos = -1
    For $i = 0 To UBound($g_aMenuText) - 1
        If $itemID = ($i + 3) Then ; Offset of 3 due to internal IDs
            $iPos = $i
            ExitLoop
        EndIf
    Next

    ; Fallback: try the itemID directly
    If $iPos < 0 Then $iPos = $itemID
    If $iPos < 0 Or $iPos >= UBound($g_aMenuText) Then $iPos = 0

    Local $sText = $g_aMenuText[$iPos]

    ; Calculate text dimensions
    Local $hDC = _WinAPI_GetDC($hWnd)
    Local $hFont = _SendMessage($hWnd, $WM_GETFONT, 0, 0)
    If Not $hFont Then $hFont = _WinAPI_GetStockObject($DEFAULT_GUI_FONT)
    Local $hOldFont = _WinAPI_SelectObject($hDC, $hFont)

    Local $tSize = _WinAPI_GetTextExtentPoint32($hDC, $sText)
    Local $iTextWidth = $tSize.X
    Local $iTextHeight = $tSize.Y

    _WinAPI_SelectObject($hDC, $hOldFont)
    _WinAPI_ReleaseDC($hWnd, $hDC)

    ; Set dimensions with padding
    $t.itemWidth = $iTextWidth + 1
    $t.itemHeight = $iTextHeight + 1

    Return 1
EndFunc   ;==>WM_MEASUREITEM_Handler

Func WM_DRAWITEM_Handler($hWnd, $iMsg, $wParam, $lParam)
    Local $tagDRAWITEM = "uint CtlType;uint CtlID;uint itemID;uint itemAction;uint itemState;ptr hwndItem;handle hDC;" & _
                         "long left;long top;long right;long bottom;ulong_ptr itemData"
    Local $t = DllStructCreate($tagDRAWITEM, $lParam)
    If Not IsDllStruct($t) Then Return $GUI_RUNDEFMSG

    If $t.CtlType <> $ODT_MENU Then Return $GUI_RUNDEFMSG

    Local $hDC     = $t.hDC
    Local $left    = $t.left
    Local $top     = $t.top
    Local $right   = $t.right
    Local $bottom  = $t.bottom
    Local $state   = $t.itemState
    Local $itemID  = $t.itemID

    ; convert itemID to position
    Local $iPos = -1
    For $i = 0 To UBound($g_aMenuText) - 1
        If $itemID = ($i + 3) Then
            $iPos = $i
            ExitLoop
        EndIf
    Next

    If $iPos < 0 Then $iPos = $itemID
    If $iPos < 0 Or $iPos >= UBound($g_aMenuText) Then $iPos = 0

    Local $sText = $g_aMenuText[$iPos]
    $sText = StringReplace($sText, "&", "")

    ; Colors
    Local $clrBG   = _ColorToCOLORREF($COLOR_BG_DARK)
    Local $clrSel  = _ColorToCOLORREF(0x505050)
    Local $clrText = _ColorToCOLORREF($COLOR_TEXT_LIGHT)

    Static $iDrawCount = 0
    Static $bFullBarDrawn = False

    ; Count how many items were drawn in this "draw cycle"
    $iDrawCount += 1

    ; If we are at the first item AND the bar has not yet been drawn
    If $iPos = 0 And Not $bFullBarDrawn Then
        ; Get the full window width
        Local $tClient = _WinAPI_GetClientRect($hWnd)
        Local $iFullWidth = DllStructGetData($tClient, "right")

        ; Fill the entire menu bar
        Local $tFullMenuBar = DllStructCreate($tagRECT)
        With $tFullMenuBar
            .left = 0
            .top = $top
            .right =  $iFullWidth + 3
            .bottom = $bottom
        EndWith

        Local $hFullBrush = _WinAPI_CreateSolidBrush($clrBG)
        _WinAPI_FillRect($hDC, $tFullMenuBar, $hFullBrush)
        _WinAPI_DeleteObject($hFullBrush)
    EndIf

    ; After drawing all items, mark as "drawn"
    If $iDrawCount >= UBound($g_aMenuText) Then
        $bFullBarDrawn = True
        $iDrawCount = 0
    EndIf

    ; Draw background for the area AFTER the last menu item
    If $iPos = (UBound($g_aMenuText) - 1) Then ; Last menu
        Local $tClient = _WinAPI_GetClientRect($hWnd)
        Local $iFullWidth = DllStructGetData($tClient, "right")

        ; Fill only the area to the RIGHT of the last menu item
        If $right < $iFullWidth Then
            Local $tEmptyArea = DllStructCreate($tagRECT)
            With $tEmptyArea
                .left = $right
                .top = $top
                .right = $iFullWidth + _WinAPI_GetSystemMetrics(7)
                .bottom = $bottom
            EndWith

            Local $hEmptyBrush = _WinAPI_CreateSolidBrush($clrBG)
            _WinAPI_FillRect($hDC, $tEmptyArea, $hEmptyBrush)
            _WinAPI_DeleteObject($hEmptyBrush)
        EndIf
    EndIf

    ; Draw item background (selected = lighter)
    Local $bSelected = BitAND($state, $ODS_SELECTED)
    Local $hBrush = _WinAPI_CreateSolidBrush($bSelected ? $clrSel : $clrBG)

    Local $tItemRect = DllStructCreate($tagRECT)
    With $tItemRect
        .left = $left
        .top = $top
        .right = $right
        .bottom = $bottom
    EndWith

    _WinAPI_FillRect($hDC, $tItemRect, $hBrush)
    _WinAPI_DeleteObject($hBrush)

    ; 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, $clrText)

    ; Draw text
    Local $tTextRect = DllStructCreate($tagRECT)
    With $tTextRect
        .left = $left + 10
        .top = $top + 4
        .right = $right - 10
        .bottom = $bottom - 4
    EndWith

    DllCall("user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $sText, "int", -1, "ptr", DllStructGetPtr($tTextRect), "uint", BitOR($DT_SINGLELINE, $DT_VCENTER, $DT_LEFT))

    If $hOldFont Then _WinAPI_SelectObject($hDC, $hOldFont)

    Return 1
EndFunc   ;==>WM_DRAWITEM_Handler

Func _WM_CTLCOLOR($hWnd, $iMsg, $wParam, $lParam)
    Local $hDC = $wParam
    Local $hCtrl = $lParam

    ; If the control is the special green label -> return green background
    If $hCtrl = $g_hLabelGreen Then
        ; black text on a green background
        _WinAPI_SetTextColor($hDC, _ColorToCOLORREF(0x000000))
        _WinAPI_SetBkColor($hDC, _ColorToCOLORREF(0x00FF00))
        _WinAPI_SetBkMode($hDC, $OPAQUE) ; important, otherwise it remains transparent and you cannot see the background
        If $g_hBrushGreen Then Return $g_hBrushGreen
    EndIf

    ; --- Special case: Make "Sample Pic" label transparent ---
    If $hCtrl = $g_hLabelPic Then
        ; set transparent background
        _WinAPI_SetBkMode($hDC, $TRANSPARENT)
        ; set text color (if necessary) - e.g., white
        _WinAPI_SetTextColor($hDC, _ColorToCOLORREF($COLOR_TEXT_LIGHT))
        ; return NULL_BRUSH (stock object), so Windows does NOT fill with your dark brush
        Local $hNull = _WinAPI_GetStockObject(5) ; 5 = NULL_BRUSH
        If $hNull Then Return $hNull
        ; Fallback if not available:
        Return $GUI_RUNDEFMSG
    EndIf

    ; --- Default behavior for all other statics / controls ---
    _WinAPI_SetTextColor($hDC, _ColorToCOLORREF($COLOR_TEXT_LIGHT))

    Local $hBrush = $g_hBrushEdit
    Local $iColor = $COLOR_EDIT_BG

    Switch $iMsg
        Case $WM_CTLCOLORBTN
            $hBrush = $g_hBrushButton
            $iColor = $COLOR_BUTTON_BG
        Case $WM_CTLCOLORSTATIC
            $hBrush = $g_hBrushBg
            $iColor = $COLOR_BG_DARK
    EndSwitch
    _WinAPI_SetBkColor($hDC, _ColorToCOLORREF($iColor))
    _WinAPI_SetBkMode($hDC, $TRANSPARENT)

    Return $hBrush
EndFunc   ;==>_WM_CTLCOLOR

Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam
    Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR, $tInfo, $tBuffer, $tBuffer2, $iCtrl
    $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    $hWndFrom = HWnd($tNMHDR.hWndFrom)
    $iIDFrom = $tNMHDR.IDFrom
    $iCode = $tNMHDR.Code

    Switch $hWndFrom
        Case $g_hDate ;thanks to argumentum for the code :-)
            Switch $iCode
                Case $NM_SETFOCUS
                    ; Disable the visual theme when the DateTime control receives focus
                    _WinAPI_SetThemeAppProperties(0)

                Case $DTN_DROPDOWN
                    ; Apply dark colors when the calendar dropdown appears
                    _WinAPI_SetWindowTheme($iCtrl, "", "")
                    Local $iCtrl = _GUICtrlDTP_GetMonthCal($hWndFrom)
                    _GUICtrlMonthCal_SetColor($iCtrl, $MCSC_TEXT, $COLOR_TEXT_LIGHT)
                    _GUICtrlMonthCal_SetColor($iCtrl, $MCSC_TITLEBK, $COLOR_BG_DARK)
                    _GUICtrlMonthCal_SetColor($iCtrl, $MCSC_TITLETEXT, $COLOR_TEXT_LIGHT)
                    _GUICtrlMonthCal_SetColor($iCtrl, $MCSC_MONTHBK, $COLOR_BG_DARK)
                    _GUICtrlMonthCal_SetColor($iCtrl, $MCSC_TRAILINGTEXT, $COLOR_TEXT_LIGHT)

                Case $DTN_CLOSEUP
                    ;  Calendar will closed

            EndSwitch
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc  ;==>WM_NOTIFY

Func _CleanupBrushes()
    ; Delete all brushes
    If $g_hBrushEdit Then _WinAPI_DeleteObject($g_hBrushEdit)
    If $g_hBrushButton Then _WinAPI_DeleteObject($g_hBrushButton)
    If $g_hBrushBg Then _WinAPI_DeleteObject($g_hBrushBg)
    If $g_hBrushGreen Then _WinAPI_DeleteObject($g_hBrushGreen)
EndFunc   ;==>_CleanupBrushes

Func _EnableDarkScrollBars()
    ; Try to enable Dark Mode for all scrollbars (also for TreeView with checkboxes)
    For $i = 0 To $g_iControlCount - 1
        Local $hCtrl = $g_aControls[$i][1]
        If Not $hCtrl Then ContinueLoop

        ; 1️ Normal attempt (works for most controls)
        Local $tScrollInfo = _GUIScrollBars_GetScrollInfoEx($hCtrl, 1)
        If IsDllStruct($tScrollInfo) Then
            _WinAPI_SetWindowTheme($hCtrl, "DarkMode_Explorer", 0)
        EndIf

        ; 2️ Extension: If the control has its own scrollbar child windows (e.g., TreeView with $TVS_CHECKBOXES)
        Local $hChild = _WinAPI_GetWindow($hCtrl, $GW_CHILD)
        While $hChild
            Local $sClass = _WinAPI_GetClassName($hChild)
            If StringCompare($sClass, "ScrollBar") = 0 Then
                ; Set DarkMode on the ScrollBar itself
                _WinAPI_SetWindowTheme($hChild, "DarkMode_Explorer", 0)
                _WinAPI_AllowDarkModeForWindow($hChild, True)
            EndIf
            $hChild = _WinAPI_GetWindow($hChild, $GW_HWNDNEXT)
        WEnd
    Next
EndFunc   ;==>_EnableDarkScrollBars

Func _WinProc($hWnd, $iMsg, $wParam, $lParam)
    ; Custom window procedure for tab control with Dark Mode
    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1 ; Prevent background erase to avoid flicker

        Case $WM_PAINT
            Local $tPaint = DllStructCreate($tagPAINTSTRUCT)
            Local $hDC = DllCall("user32.dll", "handle", "BeginPaint", "hwnd", $hWnd, "struct*", $tPaint)
            If @error Or Not $hDC[0] Then Return _WinAPI_CallWindowProc($g_hProc, $hWnd, $iMsg, $wParam, $lParam)
            $hDC = $hDC[0]

            ; Get client rectangle
            Local $tClient = _WinAPI_GetClientRect($hWnd)
            If Not IsDllStruct($tClient) Then
                _WinAPI_EndPaint($hWnd, $tPaint)
                Return 0
            EndIf

            Local $iWidth = $tClient.Right
            Local $iHeight = $tClient.Bottom

            ; Create memory DC for double buffering
            Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
            Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iWidth, $iHeight)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

            ; Fill background
            Local $hBrush = _WinAPI_CreateSolidBrush(_ColorToCOLORREF($COLOR_BG_DARK))
            _WinAPI_FillRect($hMemDC, $tClient, $hBrush)
            _WinAPI_DeleteObject($hBrush)

            ; Get tab info
            Local $iTabCount = _SendMessage($hWnd, 0x1304, 0, 0) ; TCM_GETITEMCOUNT
            Local $iCurSel = _SendMessage($hWnd, 0x130B, 0, 0)   ; TCM_GETCURSEL

            ; 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, _ColorToCOLORREF($COLOR_TEXT_LIGHT))

            ; Draw each tab
            Local $tRect, $tRect, $iLeft, $iTop, $iRight, $iBottom
            For $i = 0 To $iTabCount - 1
                ; Get tab rectangle using TCM_GETITEMRECT
                $tRect = DllStructCreate($tagRECT)
                $aResult = DllCall("user32.dll", "lresult", "SendMessageW", _
                                                            "hwnd", $hWnd, _
                                                            "uint", 0x130A, _  ; TCM_GETITEMRECT
                                                            "wparam", $i, _
                                                            "struct*", $tRect)
                If @error Or Not $aResult[0] Then ContinueLoop

                $iLeft = $tRect.Left
                $iTop = $tRect.Top
                $iRight = $tRect.Right
                $iBottom = $tRect.Bottom

                ; Skip if rectangle is invalid
                If $iLeft >= $iRight Or $iTop >= $iBottom Then ContinueLoop

                ; Get tab text
                Local $tItem = DllStructCreate("uint Mask;dword dwState;dword dwStateMask;ptr pszText;int cchTextMax;int iImage;lparam lParam")
                Local $tText = DllStructCreate("wchar Text[256]")
                With $tItem
                    .Mask = 0x0001 ; TCIF_TEXT
                    .pszText = DllStructGetPtr($tText)
                    .cchTextMax = 256
                EndWith

                DllCall("user32.dll", "lresult", "SendMessageW", _
                                                "hwnd", $hWnd, _
                                                "uint", 0x133C, _  ; TCM_GETITEMW
                                                "wparam", $i, _
                                                "struct*", $tItem)

                Local $sText = DllStructGetData($tText, "Text")

                ; Draw tab background
                Local $bSelected = ($i = $iCurSel)
                Local $iTabColor = $bSelected ? $COLOR_BUTTON_BG : $COLOR_BG_DARK
                Local $hTabBrush = _WinAPI_CreateSolidBrush(_ColorToCOLORREF($iTabColor))

                Local $tTabRect = DllStructCreate($tagRECT)
                With $tTabRect
                    .Left = $iLeft
                    .Top = $iTop
                    .Right = $iRight
                    .Bottom = $iBottom
                EndWith

                _WinAPI_FillRect($hMemDC, $tTabRect, $hTabBrush)
                _WinAPI_DeleteObject($hTabBrush)

;~              Local $hTabPen = _WinAPI_CreatePen(0, 1, _ColorToCOLORREF($COLOR_BORDER))
;~              Local $hOldTabPen = _WinAPI_SelectObject($hMemDC, $hTabPen)
;~              _WinAPI_MoveTo($hMemDC, $iLeft, $iBottom)
;~              _WinAPI_LineTo($hMemDC, $iRight, $iBottom)
;~              _WinAPI_SelectObject($hMemDC, $hOldTabPen)
;~              _WinAPI_DeleteObject($hTabPen)

                ; Draw selection indicator (top border for selected tab)
                If $bSelected Then
                    Local $hPen = _WinAPI_CreatePen(0, 2, _ColorToCOLORREF(0x0078D4)) ; Blue accent
                    Local $hOldPen = _WinAPI_SelectObject($hMemDC, $hPen)
                    _WinAPI_MoveTo($hMemDC, $iLeft, $iTop)
                    _WinAPI_LineTo($hMemDC, $iRight - 2, $iTop)
                    _WinAPI_SelectObject($hMemDC, $hOldPen)
                    _WinAPI_DeleteObject($hPen)
                EndIf

                ; Draw separator between tabs
                If $i < $iTabCount - 1 Then
                    Local $hPenSep = _WinAPI_CreatePen(0, 1, _ColorToCOLORREF($COLOR_BORDER))
                    Local $hOldPenSep = _WinAPI_SelectObject($hMemDC, $hPenSep)
                    _WinAPI_MoveTo($hMemDC, $iRight - 1, $iTop + 4)
                    _WinAPI_LineTo($hMemDC, $iRight - 1, $iBottom - 4)
                    _WinAPI_SelectObject($hMemDC, $hOldPenSep)
                    _WinAPI_DeleteObject($hPenSep)
                EndIf

                ; Draw text centered in tab
                Local $tTextRect = DllStructCreate($tagRECT)
                With $tTextRect
                    .Left = $iLeft + 6
                    .Top = $iTop + 3
                    .Right = $iRight - 6
                    .Bottom = $iBottom - 3
                EndWith

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

            ; Draw border around entire control
            Local $hBorderPen = _WinAPI_CreatePen(0, 1, _ColorToCOLORREF($COLOR_BORDER))
            Local $hOldBorderPen = _WinAPI_SelectObject($hMemDC, $hBorderPen)
            Local $hNullBrush = _WinAPI_GetStockObject(5) ; NULL_BRUSH
            Local $hOldBorderBrush = _WinAPI_SelectObject($hMemDC, $hNullBrush)

            DllCall("gdi32.dll", "bool", "Rectangle", _
                                        "handle", $hMemDC, _
                                        "int", 0, _
                                        "int", 0, _
                                        "int", $iWidth, _
                                        "int", $iHeight)

            _WinAPI_SelectObject($hMemDC, $hOldBorderPen)
            _WinAPI_SelectObject($hMemDC, $hOldBorderBrush)
            _WinAPI_DeleteObject($hBorderPen)

            ; Copy to screen
            _WinAPI_BitBlt($hDC, 0, 0, $iWidth, $iHeight, $hMemDC, 0, 0, $SRCCOPY)

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

            _WinAPI_EndPaint($hWnd, $tPaint)
            Return 0



    EndSwitch

    Return _WinAPI_CallWindowProc($g_hProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>_WinProc

Func _WinAPI_FindWindowEx($hParent, $hAfter, $sClass, $sTitle = "")
    Local $ret = DllCall("user32.dll", "hwnd", "FindWindowExW", "hwnd", $hParent, "hwnd", $hAfter, "wstr", $sClass, "wstr", $sTitle)
    If @error Or Not IsArray($ret) Then Return 0
    Return $ret[0]
EndFunc   ;==>_WinAPI_FindWindowEx


#Region DarkMode API
Func _WinAPI_ShouldAppsUseDarkMode()
    Local $aResult = DllCall("UxTheme.dll", "bool", 132)
    If @error Then Return SetError(1, 0, False)
    Return ($aResult[0] <> 0)
EndFunc   ;==>_WinAPI_ShouldAppsUseDarkMode

Func _WinAPI_AllowDarkModeForWindow($hWND, $bAllow = True)
    Local $aResult = DllCall("UxTheme.dll", "bool", 133, "hwnd", $hWND, "bool", $bAllow)
    If @error Then Return SetError(1, 0, False)
    Return ($aResult[0] <> 0)
EndFunc   ;==>_WinAPI_AllowDarkModeForWindow

Func _WinAPI_FlushMenuThemes()
    Local $aResult = DllCall("UxTheme.dll", "none", 136)
    If @error Then Return SetError(1, 0, False)
    Return True
EndFunc   ;==>_WinAPI_FlushMenuThemes

Func _WinAPI_RefreshImmersiveColorPolicyState()
    Local $aResult = DllCall("UxTheme.dll", "none", 104)
    If @error Then Return SetError(1, 0, False)
    Return True
EndFunc   ;==>_WinAPI_RefreshImmersiveColorPolicyState

Func _WinAPI_IsDarkModeAllowedForWindow($hWND)
    Local $aResult = DllCall("UxTheme.dll", "bool", 137, "hwnd", $hWND)
    If @error Then Return SetError(1, 0, False)
    Return ($aResult[0] <> 0)
EndFunc   ;==>_WinAPI_IsDarkModeAllowedForWindow

Func _WinAPI_GetIsImmersiveColorUsingHighContrast($iIMMERSIVE_HC_CACHE_MODE)
    Local $aResult = DllCall("UxTheme.dll", "bool", 106, "long", $iIMMERSIVE_HC_CACHE_MODE)
    If @error Then Return SetError(1, 0, False)
    Return ($aResult[0] <> 0)
EndFunc   ;==>_WinAPI_GetIsImmersiveColorUsingHighContrast

Func _WinAPI_OpenNcThemeData($hWND, $tClassList)
    Local $aResult = DllCall("UxTheme.dll", "hwnd", 49, "hwnd", $hWND, "struct*", $tClassList)
    If @error Then Return SetError(1, 0, False)
    Return $aResult[0]
EndFunc   ;==>_WinAPI_OpenNcThemeData

Func _WinAPI_ShouldSystemUseDarkMode()
    Local $aResult = DllCall("UxTheme.dll", "bool", 138)
    If @error Then Return SetError(1, 0, False)
    Return ($aResult[0] <> 0)
EndFunc   ;==>_WinAPI_ShouldSystemUseDarkMode

Func _WinAPI_IsDarkModeAllowedForApp()
    Local $aResult = DllCall("UxTheme.dll", "bool", 139)
    If @error Then Return SetError(1, 0, False)
    Return ($aResult[0] <> 0)
EndFunc   ;==>_WinAPI_IsDarkModeAllowedForApp

Func _WinAPI_AllowDarkModeForApp($bAllow = True) ;Windows 10 Build 17763
    Return _WinAPI_SetPreferredAppMode($bAllow ? 1 : 0) ; 1 = AllowDark, 0 = Default
EndFunc   ;==>_WinAPI_AllowDarkModeForApp

Func _WinAPI_SetPreferredAppMode($iPreferredAppMode) ;Windows 10 Build 18362+
    Local $aResult = DllCall("UxTheme.dll", "long", 135, "long", $iPreferredAppMode)
    If @error Then Return SetError(1, 0, False)
    Return $aResult[0]
EndFunc   ;==>_WinAPI_SetPreferredAppMode
#EndRegion DarkMode API

 

Sample-Controls-Dark-Mode.jpgSample-Controls-o.jpg

 

Does anyone know how to switch the date picker to dark mode?

Edited by UEZ
Update

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Posted

There is some discussion here. I am not sure how much moder menu raw is used there. What was left as I xan remeber was the white bottom line. I think them menu needs to be ownerdrawn. I do not if that is the case for all other common controls and dark mode.

Posted (edited)

@UEZ Excellent work!

 

EDIT: I just spent around an hour studying the code. The more that I read, the more my jaw dropped to the floor. Very impressive.

I may have to find an excuse to create an app that uses tabs now. I had always dreamed of dark mode tabs in AutoIt for quite a while now. 😃

I noticed one problem. I see that you've added code to paint over the white line. However, I still see the white line under the menubar.

 

EDIT2: I changed the color of the painted line so that I could see where it was ending up and get a screenshot for you.

 

image.png.b83e022422e0bc392b23c8634f87ac2b.png

 

EDIT3: I've been using the following to get the height of the menubar recently which works when DPI is used or not:

; get menubar height
    $aMenubar = _GUICtrlMenu_GetMenuBarInfo($g_hGUI)
    $iMenubarHeight = $aMenubar[3] - $aMenubar[1]
    ConsoleWrite("menubar height: " & $iMenubarHeight & @CRLF)

 

I wonder if it would be possible to measure the height of the titlebar combined with the height of the menubar to determine the coordinates to paint over the white line. The menubar height measurement is accurate. However, I have never been able to find an accurate measurement yet for the titlebar though.

Edited by WildByDesign
Posted (edited)

Can you please try this?

Func _OverpaintWhiteLine()
    Local $hDC = _WinAPI_GetWindowDC($g_hGUI)
    If Not $hDC Then Return

    Local $tWndRect = _WinAPI_GetWindowRect($g_hGUI)
    Local $iWndWidth = $tWndRect.right - $tWndRect.left

    ; 1. Caption height
    Local $iCaptionHeight = _WinAPI_GetSystemMetrics($SM_CYCAPTION)

    ; 2. Border height (top)
    Local $iBorderHeight = _WinAPI_GetSystemMetrics($SM_CYSIZEFRAME)
    If $iBorderHeight = 0 Then $iBorderHeight = _WinAPI_GetSystemMetrics($SM_CYFIXEDFRAME)

    ; 3. Determine menu height dynamically
    Local $iMenuHeight = _WinAPI_GetSystemMetrics($SM_CYMENU) ; standard menu height

    ; Alternative: get menu height via GetMenuBarInfo
    Local $tMenuBarInfo = DllStructCreate("dword cbSize;long left;long top;long right;long bottom;handle hwndMenu;handle hwndItem;bool fBarFocused;bool fFocused")
    DllStructSetData($tMenuBarInfo, "cbSize", DllStructGetSize($tMenuBarInfo))
    Local $aResult = DllCall("user32.dll", "bool", "GetMenuBarInfo", "hwnd", $g_hGUI, "long", 0xFFFFFFFD, "long", 0, "ptr", DllStructGetPtr($tMenuBarInfo))

    If IsArray($aResult) And $aResult[0] Then
        ; Calculate the actual menu height from the coordinates
        Local $iMenuTop = $tMenuBarInfo.top
        Local $iMenuBottom = $tMenuBarInfo.bottom
        Local $tWndPos = _WinAPI_GetWindowRect($g_hGUI)
        Local $iWndTop = $tWndPos.top

        ; Convert to window coordinates
        $iMenuHeight = ($iMenuBottom - $iMenuTop)
    EndIf

    ; The white line is directly below the menu
    Local $iWhiteLineY = $iCaptionHeight + $iBorderHeight + $iMenuHeight - _WinAPI_GetSystemMetrics($SM_CYFIXEDFRAME) * 2

    ConsoleWrite("Caption: " & $iCaptionHeight & ", Border: " & $iBorderHeight & ", Menu: " & $iMenuHeight & " -> White Line at Y=" & $iWhiteLineY & @CRLF)

    ; Overpaint the white line (1–2 pixels)
    Local $tRect = DllStructCreate($tagRECT)
    With $tRect
        .left = 0
        .top = $iWhiteLineY
        .right = $iWndWidth
        .bottom = $iWhiteLineY + 2 ; 2 pixels high
    EndWith

;~     Local $hBrush = _WinAPI_CreateSolidBrush(_ColorToCOLORREF($COLOR_BG_DARK))
    Local $hBrush = _WinAPI_CreateSolidBrush(_ColorToCOLORREF(0xFF0000))
    _WinAPI_FillRect($hDC, $tRect, $hBrush)
    _WinAPI_DeleteObject($hBrush)

    _WinAPI_ReleaseDC($g_hGUI, $hDC)
EndFunc   ;==>_OverpaintWhiteLine

I marked the line in red to see where it is painted. Is it now calculated properly?

Edited by UEZ
small update

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Posted (edited)
1 hour ago, UEZ said:

I marked the line in red to see where it is painted. Is it now calculated properly?

This works properly, yes. Thank you. It also worked once I applied DPI scaling of 125%. But once I tried 150% and higher the line did not line up anymore. But I understand that it is not supporting DPI yet.

EDIT: By the way, I wanted to mention that not only is your coding incredible, but I really wanted to point out the fact that your code comments are also very easy to understand and therefore helpful for anyone reading your code. I appreciate the extra effort that you put into code comments. :)

Edited by WildByDesign
Posted
On 10/14/2025 at 2:51 PM, UEZ said:

Does anyone know how to switch the date picker to dark mode?

Nobody in AutoIt has had success with this.

I was looking around at some open source projects that have great dark mode support (Notepad++, Explorer++, System Informer, etc.) to see if there was any source code examples but unfortunately none of these projects utilize SysDateTimePick32 from what I can tell.

Posted
6 hours ago, argumentum said:

Yes, thanks 👍

 

Updated the code for the window when date pick icon was clicked. Still searching for a way to change the date control itself.

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Posted

By the way, Windows 11 stable channel build 25H2 (and probably 24H2), build 26200.6899, just added brand new visual styles in aero.msstyles version 10.0.26100.6725 that contains all of the new dark mode classes.

DarkMode_CopyEngine::Progress
DarkMode_CopyEngine_Indeterminate::Progress
DarkMode_DarkTheme::Button
DarkMode_DarkTheme::Combobox
DarkMode_DarkTheme::Edit
DarkMode_DarkTheme::Header
DarkMode_DarkTheme::Link
DarkMode_DarkTheme::Listbox
DarkMode_DarkTheme::ListView
DarkMode_DarkTheme::Progress
DarkMode_DarkTheme::Rebar
DarkMode_DarkTheme::ScrollBar
DarkMode_DarkTheme::Spin
DarkMode_DarkTheme::Static
DarkMode_DarkTheme::Status
DarkMode_DarkTheme::Tab
DarkMode_DarkTheme::TaskDialog
DarkMode_DarkTheme::Toolbar
DarkMode_DarkTheme::Tooltip
DarkMode_DarkTheme::TreeView
DarkMode_DarkTheme_Indeterminate::Progress

Keep in mind, the DarkMode_DarkTheme classes are completely new and different from the DarkMode_Explorer classes that we are used to using.

Posted (edited)
On 10/14/2025 at 2:51 PM, UEZ said:
                Case "msctls_progress32"
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_Explorer", 0)

This no longer works on the latest stable channel builds of Windows 11. In particular, builds 24H2 (26100.6899) and 25H2 (26200.6899), it fails to theme the progress control at all. (I just get an empty white box)

On these most recent stable channel builds of Windows 11, it now has to be:

Case "msctls_progress32"
                    _WinAPI_SetWindowTheme($hCtrl, "DarkMode_CopyEngine", 0)

With your example for SampleControls and also the DarkMode UDF, we are going to have to start comparing the builds with macro @OSBuild to determine which theme class to use.

These newer DarkMode_CopyEngine* and DarkMode_DarkTheme* theme classes seem to be improved significantly over the former DarkMode_Explorer* classes. A lot of visual issues are fixed now as well. So for any users who are on Windows 11 with the latest windows updates, using DarkMode_DarkTheme* is by far the better option over DarkMode_Explorer* classes.

MS even added nice dark theme for statusbar as well in DarkMode_DarkTheme* class. ListViews are improved too.

I'm not sure if we should update the older DarkMode UDF or start a new one. In my opinion, of the older DarkMode UDF versions, the GUIDarkMode_v0.02mod.au3 from the fabulous @argumentum is the most functional and best option to use as a base.

Edited by WildByDesign

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
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...