Jump to content

Recommended Posts

Posted
11 minutes ago, UEZ said:

Another white line seems to occur at 200% above the menus

I believe I have seen this before, although I do not think it is related to the infamous white line at the bottom that all win32-darkmode apps have had to handle.

I don't have 200% DPI monitor to test. However, I have a feeling some adjustments may be needed for 200% and above to the _WM_MEASUREITEM() function. Specifically:

$t.itemHeight = $iTextHeight + 0.5

You could try different changes there to see if it covers up that top white area. This is just a guess without being able to test at 200% myself.

Posted (edited)

I really like the customdraw code for the slider/trackbar control. I do have a suggestion to help improve the usability of it. I find that I don't always click on it properly because it gives no indication of whether the cursor is over the thumb or not.

In Case $TBCD_THUMB, if possible, I would suggest adding a slightly different color for when the cursor is over the thumb. I don't know exactly how to do it. But I do know that the Case $TBCD_THUMB gets hit up anytime the cursor goes over the thumb specifically.

EDIT: All of the trackbar states can be found here although I do not know what their values are.

EDIT2: Also, if you don't want to draw the thumb, you can get it from the theme resources:

Local $tRECT = _WinAPI_CreateRectEx(0, 0, 15, 24) ; based on my 125% scaling
_WinAPI_SetBkMode($hDC, $TRANSPARENT)
Local $hTheme = _WinAPI_OpenThemeData($hForm, 'TrackBar')
;Local $tSIZE = __WinAPI_GetThemePartSize($hTheme, $TKP_THUMBBOTTOM, 0, Null, Null, $TS_TRUE) ; need to get tRECT here
_WinAPI_DrawThemeBackground($hTheme, 4, 0, $hDC, $tRECT) ; active
;_WinAPI_DrawThemeBackground($hTheme, 4, 3, $hDC, $tRECT) ; hover?

 

Edited by WildByDesign
Posted (edited)

This should do the trick:

...
Func _WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
...
    ; --- Slider (msctls_trackbar32) custom drawing ---
    If $iCode = $NM_CUSTOMDRAW And StringLower(_WinAPI_GetClassName($hWndFrom)) = "msctls_trackbar32" Then
        Local $tNMCD = DllStructCreate($tagNMCUSTOMDRAW, $lParam)
        Local $dwStage = $tNMCD.dwDrawStage
        Local $hDC = $tNMCD.hdc
        Local $dwItemSpec = $tNMCD.dwItemSpec
        
        Switch $dwStage
            Case $CDDS_PREPAINT
                $tNMCD.ItemState = BitXOR($tNMCD.ItemState, $CDIS_FOCUS)
                Return $CDRF_NOTIFYSUBITEMDRAW

            Case 0x00010001 ;BitOR($CDDS_SUBITEM, $CDDS_ITEMPREPAINT)
                Switch $dwItemSpec
                    Case $TBCD_THUMB
                        Local $iL = $tNMCD.left
                        Local $iT = $tNMCD.top
                        Local $iR = $tNMCD.right - 1
                        Local $iB = $tNMCD.bottom
                        Local $iMid   = ($iL + $iR) / 2
                        Local $iSplit = $iB - ($iR - $iL) / 2

                        ; Hover check via cursor position
                        Local $tPt = DllStructCreate($tagPOINT)
                        DllCall("user32.dll", "bool", "GetCursorPos", "struct*", $tPt)
                        DllCall("user32.dll", "bool", "ScreenToClient", "hwnd", $hWndFrom, "struct*", $tPt)
                        Local $bHot = ($tPt.X >= $iL And $tPt.X <= $iR And $tPt.Y >= $iT And $tPt.Y <= $iB)

                        Local $tPoints = DllStructCreate("int p[10]")
                        $tPoints.p((1)) = $iL
                        $tPoints.p((2)) = $iT
                        $tPoints.p((3)) = $iR
                        $tPoints.p((4)) = $iT
                        $tPoints.p((5)) = $iR
                        $tPoints.p((6)) = $iSplit
                        $tPoints.p((7)) = $iMid
                        $tPoints.p((8)) = $iB
                        $tPoints.p((9)) = $iL
                        $tPoints.p((10)) = $iSplit

                        Local $hBrush = _WinAPI_CreateSolidBrush(_ColorToCOLORREF($bHot ? $COLOR_TAB_ACCENT_LIGHT : $COLOR_TAB_ACCENT))
                        Local $hPen   = _WinAPI_CreatePen(0, 1, _ColorToCOLORREF($COLOR_CONTROL_BG))
                        Local $hOldBrush = _WinAPI_SelectObject($hDC, $hBrush)
                        Local $hOldPen   = _WinAPI_SelectObject($hDC, $hPen)
                        DllCall("gdi32.dll", "bool", "Polygon", "handle", $hDC, "struct*", $tPoints, "int", 5)
                        _WinAPI_SelectObject($hDC, $hOldBrush)
                        _WinAPI_SelectObject($hDC, $hOldPen)
                        _WinAPI_DeleteObject($hBrush)
                        _WinAPI_DeleteObject($hPen)
                        Return $CDRF_SKIPDEFAULT

                    Case $TBCD_CHANNEL
                        Local $hBrush = _WinAPI_CreateSolidBrush(_ColorToCOLORREF($COLOR_MENU_SELECT))
                        Local $tRECT2 = DllStructCreate($tagRECT)
                        $tRECT2.Left = $tNMCD.left
                        $tRECT2.Top    = $tNMCD.top
                        $tRECT2.Right = $tNMCD.right
                        $tRECT2.Bottom = $tNMCD.bottom
                        _WinAPI_FillRect($hDC, $tRECT2, $hBrush)
                        _WinAPI_DeleteObject($hBrush)
                        Return $CDRF_SKIPDEFAULT

                    Case Else
                        Return $CDRF_DODEFAULT ; channel + ticks drawn by Windows
                EndSwitch
        EndSwitch
    EndIf
...

 

Edited by UEZ

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)
2 hours ago, UEZ said:

This should do the trick:

This works wonderfully. Thank you.

EDIT: After further testing, there appears to be one bug with this. If you have the cursor on the thumb and move the cursor off to the left, right or above the thumb, the color restores properly 100% of the time. Perfect.

However, if you have the cursor on the thumb and move the cursor straight down off of the thumb, the color only restores properly approx. 50% of the time. It gets stuck with the hot color in that case.

EDIT2: This seems to fix the issue 100%: (adding - 1)

Local $bHot = ($tPt.X >= $iL And $tPt.X <= $iR And $tPt.Y >= $iT And $tPt.Y <= $iB - 1)

 

Edited by WildByDesign
Posted (edited)

I have a usability idea to improve the menu. Generally, all programs in Windows with a menubar will have the menubar item text go dim when it is not the foreground window (similar to the titlebar text). This is something that we are lacking and even ModernMenuRaw UDF did not do.

Try something like this within your _WM_DRAWITEM and adjust to how you prefer:

_WinAPI_SetBkMode($hDC, $TRANSPARENT)
; ...
If _WinAPI_GetForegroundWindow() <> $g_hGUI Then $clrText = _WinAPI_ColorAdjustLuma($clrText, -30)
; ...
_WinAPI_SetTextColor($hDC, $clrText)

I find that it works quite well. I'm not sure what the text color should be exactly, but that can be adjusted to however you think is best.

Edited by WildByDesign
Posted
2 hours ago, WildByDesign said:

I have a usability idea to improve the menu. Generally, all programs in Windows with a menubar will have the menubar item text go dim when it is not the foreground window (similar to the titlebar text). This is something that we are lacking and even ModernMenuRaw UDF did not do.

Try something like this within your _WM_DRAWITEM and adjust to how you prefer:

_WinAPI_SetBkMode($hDC, $TRANSPARENT)
; ...
If _WinAPI_GetForegroundWindow() <> $g_hGUI Then $clrText = _WinAPI_ColorAdjustLuma($clrText, -30)
; ...
_WinAPI_SetTextColor($hDC, $clrText)

I find that it works quite well. I'm not sure what the text color should be exactly, but that can be adjusted to however you think is best.

Nice idea - added to DPI code.

I've a MsgBox in the example when you select About in the menus which is not in dark mode. I created a MsgBoxEx to make also the Windows MsgBox dark.

One issue is when you added timer to the MsgBoxEx and drag it around when counter reaches 0 and the MsgBox is closed it will cause a crash -> 0xC000041D

It is more or less this code:

;Coded by UEZ build 2026-03-19 beta
#AutoIt3Wrapper_Run_Au3Stripper=y
#Au3Stripper_Parameters=/so
#Au3Stripper_Ignore_Funcs=_TimerProc

#include <ButtonConstants.au3>
#include <Timers.au3>
#include <WinAPIConstants.au3>
#include <WinAPIGdi.au3>
#include <WinAPIProc.au3>
#include <WinAPISys.au3>
#include <WinAPISysWin.au3>
#include <WinAPITheme.au3>
#include <WindowsConstants.au3>

Global Const $BS_PUSHBUTTON = 0x000000
Global Const $COLOR_BG_DARK = 0x202020 
Global Const $COLOR_TEXT_LIGHT = 0xF0F0F0 
Global Const $COLOR_HOTTRACK_MENU = 0x3A3A3A 

Const $HCBT_CREATEWND = 3, $HCBT_DESTROYWND = 4, $HCBT_ACTIVATE = 5, $g_iFlagDefault = BitOR($MB_TOPMOST, $MB_ICONINFORMATION)
Global $g_hMsgBoxHook, $g_hSubMsgBox, $g_idTImer, $g_sBtn1_Txt = "Close", $g_sBtn2_Txt, $g_sBtn3_Txt
Global $g_Timeout = 0, $g_hMsgBoxOldProc, $g_hMsgBoxBrush, $g_hMsgBoxBtn = 0, $g_bMsgBoxClosing = False
Global $g_hMsgBoxSubProc = DllCallbackRegister("_MsgBoxProc", "lresult", "hwnd;uint;wparam;lparam")

Func _WinAPI_SetDlgItemText($hDlg, $nIDDlgItem, $lpString) ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setdlgitemtextw
    Local $aRet = DllCall("user32.dll", "int", "SetDlgItemText", "hwnd", $hDlg, "int", $nIDDlgItem, "str", $lpString)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aRet[0]
EndFunc   ;==>_WinAPI_SetDlgItemText

Func _WinAPI_FindWindowEx($hParent, $sClass, $sTitle = "", $hAfter = 0)
    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

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 _MsgBoxProc($hWnd, $iMsg, $wParam, $lParam)
    If Not $g_hMsgBoxOldProc Then Return _WinAPI_DefWindowProc($hWnd, $iMsg, $wParam, $lParam)
    Switch $iMsg
        Case $WM_CTLCOLORSTATIC, $WM_CTLCOLORDLG, $WM_CTLCOLORBTN
            _WinAPI_SetTextColor($wParam, _ColorToCOLORREF($COLOR_TEXT_LIGHT))
            _WinAPI_SetBkColor($wParam, _ColorToCOLORREF($COLOR_BG_DARK))
            Return $g_hMsgBoxBrush

        Case $WM_ERASEBKGND
            ; Paint entire dialog background including button area
            Local $tRECT = _WinAPI_GetClientRect($hWnd)     
            _WinAPI_FillRect($wParam, $tRECT, $g_hMsgBoxBrush)
            Return 1
            
        Case $WM_PAINT
            Local $iRet = _WinAPI_CallWindowProc($g_hMsgBoxOldProc, $hWnd, $iMsg, $wParam, $lParam)
            Local $hDC = _WinAPI_GetDC($hWnd)
            Local $tRECT = _WinAPI_GetClientRect($hWnd), $tRECT2
            $tRECT.top = $tRECT.bottom - 42
            Local $hBrushFooter = _WinAPI_CreateSolidBrush(_ColorToCOLORREF($COLOR_HOTTRACK_MENU))
            _WinAPI_FillRect($hDC, $tRECT, $hBrushFooter)
            _WinAPI_ReleaseDC($hWnd, $hDC)
            _WinAPI_DeleteObject($hBrushFooter)
            Return $iRet
    
        Case $WM_CLOSE
            If $g_idTImer Then
                _Timer_KillTimer($hWnd, $g_idTImer)
                $g_idTImer = 0
            EndIf
    
        Case $WM_DESTROY
            If $g_idTImer Then
                _Timer_KillTimer($hWnd, $g_idTImer)
                $g_idTImer = 0
            EndIf
            _WinAPI_SetWindowLong($hWnd, $GWL_WNDPROC, $g_hMsgBoxOldProc)
            _WinAPI_DeleteObject($g_hMsgBoxBrush)
            $g_hMsgBoxBrush = 0
    EndSwitch

    Return _WinAPI_CallWindowProc($g_hMsgBoxOldProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc

Func _TimerProc($hWnd, $iMsg, $wParam, $lParam)
    If $g_bMsgBoxClosing Then Return

    If $g_Timeout <= 1 Then
        $g_bMsgBoxClosing = True
        _Timer_KillTimer($hWnd, $g_idTImer)
        _WinAPI_PostMessage($hWnd, $WM_CLOSE, 0, 0)
    Else
        $g_Timeout -= 1
        _WinAPI_SetDlgItemText($hWnd, $IDOK + 1, $g_sBtn1_Txt & " [" & $g_Timeout & "]")
    EndIf
EndFunc

Func _CBTHookProc($nCode, $wParam, $lParam)
    If $nCode < 0 Then Return _WinAPI_CallNextHookEx($g_hMsgBoxHook, $nCode, $wParam, $lParam)
    Local Const $hHWND = HWnd($wParam)
    Switch $nCode
        Case $HCBT_ACTIVATE
            If _WinAPI_GetClassName($hHWND) = "#32770" Then
                If $g_Timeout Then $g_idTImer = _Timer_SetTimer($hHWND, 1000, "_TimerProc")
                _WinAPI_SetDlgItemText($wParam, $IDOK, $g_Timeout ? $g_sBtn1_Txt & " [" & $g_Timeout & "]" : $g_sBtn1_Txt)

                ; Title bar dark
                _WinAPI_DwmSetWindowAttribute($hHWND, $DWMWA_USE_IMMERSIVE_DARK_MODE, True)

                ; Dark theme current button
                $g_hMsgBoxBtn = _WinAPI_GetDlgItem($hHWND, $IDOK)
                
                _WinAPI_SetWindowTheme($g_hMsgBoxBtn, "DarkMode_Explorer", 0)
                _WinAPI_AllowDarkModeForWindow($g_hMsgBoxBtn, True)
                
                ; Dark theme for static controls (text + icon)
                Local $hStatic = _WinAPI_FindWindowEx($hHWND, "Static")
                $g_hStaticMsgBox = $hStatic
                While $hStatic
                    _WinAPI_AllowDarkModeForWindow($hStatic, True)
                    $hStatic = _WinAPI_FindWindowEx($hHWND, "Static", "", $hStatic)
                WEnd

                $g_hMsgBoxBrush = _WinAPI_CreateSolidBrush(_ColorToCOLORREF($COLOR_BG_DARK))
                $g_hMsgBoxOldProc = _WinAPI_SetWindowLong($hHWND, $GWL_WNDPROC, DllCallbackGetPtr($g_hMsgBoxSubProc))

                _WinAPI_RedrawWindow($hHWND, 0, 0, BitOR($RDW_INVALIDATE, $RDW_UPDATENOW, $RDW_ALLCHILDREN))
            EndIf
        Case $HCBT_DESTROYWND
            If _WinAPI_GetClassName($hHWND) = "#32770" Then _Timer_KillTimer($hHWND, $g_idTImer)
    EndSwitch
    Return _WinAPI_CallNextHookEx($g_hMsgBoxHook, $nCode, $wParam, $lParam)
EndFunc   ;==>_CBTHookProc

Func MsgBoxEx($sText, $sTitle = Default, $iTimeout = 0, $iFlag = Default, $sBtn_Txt = Default, $hParentHWND = "")
    If $sBtn_Txt <> Default Then $g_sBtn1_Txt = $sBtn_Txt
    If $iFlag = Default Then $iFlag = $g_iFlagDefault
    $g_Timeout = $iTimeout
    Local $hMsgProc = DllCallbackRegister("_CBTHookProc", "int", "uint;wparam;lparam")
    Local Const $hThreadID = _WinAPI_GetCurrentThreadId()
    $g_hMsgBoxHook = _WinAPI_SetWindowsHookEx($WH_CBT, DllCallbackGetPtr($hMsgProc), Null, $hThreadID)
    If $sTitle = Default Then $sTitle = "Information"
    Local Const $iReturn = MsgBox($iFlag, $sTitle, $sText, $iTimeout, $hParentHWND)
    If $g_hMsgBoxHook Then _WinAPI_UnhookWindowsHookEx($g_hMsgBoxHook)
    DllCallbackFree($hMsgProc)
    Return $iReturn
EndFunc   ;==>MsgBoxEx

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

ConsoleWrite(MsgBoxEx("This is a test", "Information", 5) & @CRLF)

Any idea how to prevend it?

 

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
50 minutes ago, UEZ said:

One issue is when you added timer to the MsgBoxEx and drag it around when counter reaches 0 and the MsgBox is closed it will cause a crash -> 0xC000041D

I personally have always wished for a proper  dark mode MsgBox, so this is quite exciting.

51 minutes ago, UEZ said:

Any idea how to prevend it?

I don’t know what is the root cause of this crash. I know that @MattyD has been doing similar work recently on additional dialogs.

Sorry to tag you in here Matty, but do you have any idea as to the source of this crash?

Posted
5 hours ago, UEZ said:

One issue is when you added timer to the MsgBoxEx and drag it around when counter reaches 0 and the MsgBox is closed it will cause a crash -> 0xC000041D

I notice a crash when the MsgBox loses focus and regains focus as well. For example, open the About msgbox. Click on the taskbar and back on the About msgbox and it crashes.

Posted

I think the main issue is that we're using $HCBT_ACTIVATE as the trigger to install everything, but it can fire several times during the life of the dialog.

When we get a HCBT_ACTIVATE, we stash a ptr to the original proc ($g_hMsgBoxOldProc) and use this to do the donkey work within _MsgBoxProc. But if HCBT_ACTIVATE fires a second time, g_hMsgBoxOldProc becomes a ptr to  our MsgBoxProc .  So that causes the func to recurse into itself until it everything falls apart.

Posted (edited)

Thanks for the hint @MattyD - it fixed the focus crash.

This seems to work - please test:

;Coded by UEZ build 2026-03-19 beta
#AutoIt3Wrapper_Run_Au3Stripper=y
#Au3Stripper_Parameters=/so
#Au3Stripper_Ignore_Funcs=_TimerProc

#include <ButtonConstants.au3>
#include <Timers.au3>
#include <WinAPIConstants.au3>
#include <WinAPIGdi.au3>
#include <WinAPIProc.au3>
#include <WinAPISys.au3>
#include <WinAPISysWin.au3>
#include <WinAPITheme.au3>
#include <WindowsConstants.au3>

Global Const $BS_PUSHBUTTON = 0x000000
Global Const $COLOR_BG_DARK = 0x202020 
Global Const $COLOR_TEXT_LIGHT = 0xF0F0F0 
Global Const $COLOR_HOTTRACK_MENU = 0x3A3A3A 
Global Const $COLOR_TITLE_DARK = 0x181818

Const $HCBT_CREATEWND = 3, $HCBT_DESTROYWND = 4, $HCBT_ACTIVATE = 5, $g_iFlagDefault = BitOR($MB_TOPMOST, $MB_ICONINFORMATION)
Global $g_hMsgBoxHook, $g_hSubMsgBox, $g_idTImer, $g_sBtn1_Txt = "Close", $g_sBtn2_Txt, $g_sBtn3_Txt
Global $g_Timeout = 0, $g_hMsgBoxOldProc, $g_hMsgBoxBrush, $g_hMsgBoxBtn = 0, $g_bMsgBoxClosing = False, $g_bNCLButtonDown = False, $g_bMsgBoxInitialized = False
Global $g_hMsgBoxSubProc = DllCallbackRegister("_MsgBoxProc", "lresult", "hwnd;uint;wparam;lparam")

Func _WinAPI_SetDlgItemText($hDlg, $nIDDlgItem, $lpString) ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setdlgitemtextw
    Local $aRet = DllCall("user32.dll", "int", "SetDlgItemText", "hwnd", $hDlg, "int", $nIDDlgItem, "str", $lpString)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aRet[0]
EndFunc   ;==>_WinAPI_SetDlgItemText

Func _WinAPI_FindWindowEx($hParent, $sClass, $sTitle = "", $hAfter = 0)
    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

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 _MsgBoxProc($hWnd, $iMsg, $wParam, $lParam)
    If Not $g_hMsgBoxOldProc Then Return _WinAPI_DefWindowProc($hWnd, $iMsg, $wParam, $lParam)
    If $g_Timeout < 0 Or Not _WinAPI_IsWindow($hWnd) Then Return _WinAPI_DefWindowProc($hWnd, $iMsg, $wParam, $lParam)
    
    Switch $iMsg
        Case $WM_CTLCOLORSTATIC, $WM_CTLCOLORDLG, $WM_CTLCOLORBTN
            If Not $g_hMsgBoxBrush Then Return _WinAPI_CallWindowProc($g_hMsgBoxOldProc, $hWnd, $iMsg, $wParam, $lParam)
            
            _WinAPI_SetTextColor($wParam, _ColorToCOLORREF($COLOR_TEXT_LIGHT))
            _WinAPI_SetBkColor($wParam, _ColorToCOLORREF($COLOR_BG_DARK))
            Return $g_hMsgBoxBrush

        Case $WM_ERASEBKGND
            If Not $g_hMsgBoxBrush Then Return _WinAPI_CallWindowProc($g_hMsgBoxOldProc, $hWnd, $iMsg, $wParam, $lParam)

            Local $tRECT = _WinAPI_GetClientRect($hWnd)     
            _WinAPI_FillRect($wParam, $tRECT, $g_hMsgBoxBrush)
            Return 1
            
        Case $WM_PAINT      
            Local $iRet = _WinAPI_CallWindowProc($g_hMsgBoxOldProc, $hWnd, $iMsg, $wParam, $lParam)
            Local $hDC = _WinAPI_GetDC($hWnd)
            Local $tRECT = _WinAPI_GetClientRect($hWnd)
            
            If $g_hMsgBoxBtn Then
                Local $tBtnRect = _WinAPI_GetWindowRect($g_hMsgBoxBtn)
                Local $tPoint = DllStructCreate($tagPOINT)
                $tPoint.x = $tBtnRect.left
                $tPoint.y = $tBtnRect.top
                _WinAPI_ScreenToClient($hWnd, $tPoint)
                $tRECT.top  = $tPoint.y - 10
            EndIf
        
            Local $hBrushFooter = _WinAPI_CreateSolidBrush(_ColorToCOLORREF($COLOR_HOTTRACK_MENU))
            _WinAPI_FillRect($hDC, $tRECT, $hBrushFooter)
            _WinAPI_ReleaseDC($hWnd, $hDC)
            _WinAPI_DeleteObject($hBrushFooter)
            Return $iRet
    
        Case $WM_COMMAND
            If $g_bNCLButtonDown And (BitAND($wParam, 0xFFFF) = $IDOK) Then
                $g_bMsgBoxClosing = True
                Return 0
            EndIf
            Return _WinAPI_CallWindowProc($g_hMsgBoxOldProc, $hWnd, $iMsg, $wParam, $lParam)
    
        Case $WM_NCLBUTTONDOWN
            $g_bNCLButtonDown = True
            Local $iRet = _WinAPI_CallWindowProc($g_hMsgBoxOldProc, $hWnd, $iMsg, $wParam, $lParam)
            $g_bNCLButtonDown = False
            If $g_bMsgBoxClosing Then
                _WinAPI_PostMessage($hWnd, $WM_COMMAND, $IDOK + 1, 0)
            EndIf
            Return $iRet
    
        Case $WM_CLOSE
            If $g_idTImer Then
                _Timer_KillTimer($hWnd, $g_idTImer)
                $g_idTImer = 0
            EndIf
        
        Case $WM_NCDESTROY
            _WinAPI_SetWindowLong($hWnd, $GWL_WNDPROC, $g_hMsgBoxOldProc)
            Local $hBrush = $g_hMsgBoxBrush
            $g_hMsgBoxBrush = 0

            If $hBrush Then
                _WinAPI_DeleteObject($hBrush)
            EndIf
            
        Case $WM_DESTROY
            If $g_idTImer Then
                _Timer_KillTimer($hWnd, $g_idTImer)
                $g_idTImer = 0
            EndIf
        
            Return _WinAPI_CallWindowProc($g_hMsgBoxOldProc, $hWnd, $iMsg, $wParam, $lParam)
    EndSwitch

    Return _WinAPI_CallWindowProc($g_hMsgBoxOldProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc

Func _TimerProc($hWnd, $iMsg, $wParam, $lParam)
    If Not _WinAPI_IsWindow($hWnd) Or $g_bMsgBoxClosing Then Return
    If $g_Timeout <= 1 Then
        $g_bMsgBoxClosing = True
        _Timer_KillTimer($hWnd, $g_idTImer)
        $g_idTImer = 0        
        If Not $g_bNCLButtonDown Then _WinAPI_PostMessage($hWnd, $WM_COMMAND, $IDOK + 1, 0)
        Return
    EndIf
    $g_Timeout -= 1
    _WinAPI_SetDlgItemText($hWnd, $IDOK + 1, $g_sBtn1_Txt & " [" & $g_Timeout & "]")
EndFunc

Func _CBTHookProc($nCode, $wParam, $lParam)
    If $nCode < 0 Then Return _WinAPI_CallNextHookEx($g_hMsgBoxHook, $nCode, $wParam, $lParam)
    Local Const $hHWND = HWnd($wParam)
    Switch $nCode
        Case $HCBT_ACTIVATE
            If _WinAPI_GetClassName($hHWND) = "#32770" Then
                If $g_bMsgBoxInitialized Then Return _WinAPI_CallNextHookEx($g_hMsgBoxHook, $nCode, $wParam, $lParam)
                $g_bMsgBoxInitialized = True
                
                If $g_Timeout Then $g_idTImer = _Timer_SetTimer($hHWND, 1000, "_TimerProc")
                _WinAPI_SetDlgItemText($wParam, $IDOK, $g_Timeout ? $g_sBtn1_Txt & " [" & $g_Timeout & "]" : $g_sBtn1_Txt)

                ; Title bar dark
                _WinAPI_DwmSetWindowAttribute($hHWND, $DWMWA_USE_IMMERSIVE_DARK_MODE, True)

                ; Dark title + caption colors
                _WinAPI_DwmSetWindowAttribute($hHWND, 20, True) ; immersive dark

                ; optional: remove bright border
                _WinAPI_DwmSetWindowAttribute($hHWND, $DWMWA_BORDER_COLOR, _ColorToCOLORREF(0x303030))
                
                ; caption color
                _WinAPI_DwmSetWindowAttribute($hHWND, $DWMWA_CAPTION_COLOR, _ColorToCOLORREF($COLOR_TITLE_DARK))
                
                ; caption text
                _WinAPI_DwmSetWindowAttribute($hHWND, $DWMWA_TEXT_COLOR, _ColorToCOLORREF($COLOR_TEXT_LIGHT))

                Local $i, $iStyle
                For $i = 0 To 7
                    $hBtn = _WinAPI_GetDlgItem($hHWND, $i)
                    If $hBtn Then
                        $g_hMsgBoxBtn = $hBtn                   
                        _WinAPI_SetWindowTheme($hBtn, "DarkMode_Explorer", 0)
                        _WinAPI_AllowDarkModeForWindow($hBtn, True)
                    EndIf
                Next
                
                ; Dark theme for static controls (text + icon)
                Local $hStatic = _WinAPI_FindWindowEx($hHWND, "Static")
                While $hStatic
                    _WinAPI_AllowDarkModeForWindow($hStatic, True)
                    $hStatic = _WinAPI_FindWindowEx($hHWND, "Static", "", $hStatic)
                WEnd

                $g_hMsgBoxBrush = _WinAPI_CreateSolidBrush(_ColorToCOLORREF($COLOR_BG_DARK))
                $g_hMsgBoxOldProc = _WinAPI_SetWindowLong($hHWND, $GWL_WNDPROC, DllCallbackGetPtr($g_hMsgBoxSubProc))

                _WinAPI_RedrawWindow($hHWND, 0, 0, BitOR($RDW_INVALIDATE, $RDW_UPDATENOW, $RDW_ALLCHILDREN))
            EndIf
        Case $HCBT_DESTROYWND
            If _WinAPI_GetClassName($hHWND) = "#32770" Then 
                $g_bMsgBoxInitialized = False
                _Timer_KillTimer($hHWND, $g_idTImer)
            EndIf
    EndSwitch
    Return _WinAPI_CallNextHookEx($g_hMsgBoxHook, $nCode, $wParam, $lParam)
EndFunc   ;==>_CBTHookProc

Func MsgBoxEx($sText, $sTitle = Default, $iTimeout = 0, $iFlag = Default, $sBtn_Txt = Default, $hParentHWND = "")
    $g_hMsgBoxBtn = 0
    $g_bMsgBoxClosing = False
    $g_bNCLButtonDown = False
    $g_bMsgBoxInitialized = False
    If $sBtn_Txt <> Default Then $g_sBtn1_Txt = $sBtn_Txt
    If $iFlag = Default Then $iFlag = $g_iFlagDefault
    $g_Timeout = $iTimeout
    Local $hMsgProc = DllCallbackRegister("_CBTHookProc", "int", "uint;wparam;lparam")
    Local Const $hThreadID = _WinAPI_GetCurrentThreadId()
    $g_hMsgBoxHook = _WinAPI_SetWindowsHookEx($WH_CBT, DllCallbackGetPtr($hMsgProc), Null, $hThreadID)
    If $sTitle = Default Then $sTitle = "Information"
    Local Const $iReturn = MsgBox($iFlag, $sTitle, $sText, 0, $hParentHWND)
    If $g_hMsgBoxHook Then _WinAPI_UnhookWindowsHookEx($g_hMsgBoxHook)
    DllCallbackFree($hMsgProc)
    Return $iReturn
EndFunc   ;==>MsgBoxEx

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

ConsoleWrite(MsgBoxEx("This is a test", "Information", 5) & @CRLF)

 

Edited by UEZ

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

I really like the technique that you use to paint the darker rectangle around the Edit and ListBox controls to cover up the brighter border. However, I feel like it takes away some of the "depth" of the control and could make the user have a harder time to know which edit/listbox control is active.

Suggestion: When the specific Edit/ListBox control is active, paint the background slightly brighter.

My thought would be to use your existing _WM_CTLCOLOR() function. If we can determine if the Edit/ListBox control has focus (keyboard), change the color of the brush that is returned from that function. This might be the best option since it would work for both Win10 and Win11 users.

I just don't know how to determine if that specific control has the focus. Possibly the control handle can be compared with _WinAPI_GetFocus() but I have not tried.

Alternative:

DarkMode_DarkTheme works perfectly now for Edit controls. Not perfect for ListBox though. But Edit controls are perfect and would not need any additional rectangle painted around them. The only downside is that it would only works for Windows 11 24H2/25H2 with latest updates.

Posted

I had a bit more of a play with that msgbox stuff today and I landed on something like this. It's not meant to be a replacement by any means, it's probably more a scratch pad for my own benefit.  But I figure someone might wish to pick though it.

#AutoIt3Wrapper_UseX64=N

#include <ButtonConstants.au3>
#include <Timers.au3>
#include <WinAPI.au3>
#include <WinAPITheme.au3>
#include <WindowsConstants.au3>
#include <WinAPIGdi.au3>

;Store COLOR_* as coloref (consistancy with _WinAPI_GetSysColor())
Global Const $COLOR_BG_DARK = _WinAPI_SwitchColor(0x202020)
Global Const $COLOR_TEXT_LIGHT = _WinAPI_SwitchColor(0xF0F0F0)
Global Const $COLOR_HOTTRACK_MENU = _WinAPI_SwitchColor(0x3A3A3A)
Global Const $COLOR_TITLE_DARK = _WinAPI_SwitchColor(0x181818)

Global Const $HCBT_MOVESIZE = 0
Global Const $HCBT_MINMAX = 1
Global Const $HCBT_QS = 2
Global Const $HCBT_CREATEWND = 3
Global Const $HCBT_DESTROYWND = 4
Global Const $HCBT_ACTIVATE = 5
Global Const $HCBT_CLICKSKIPPED = 6
Global Const $HCBT_KEYSKIPPED = 7
Global Const $HCBT_SYSCOMMAND = 8
Global Const $HCBT_SETFOCUS = 9

Global $g_hMsgBoxHook, $g_idTImer, $g_sBtn1_Txt = "Close"
Global $g_UseDarkMode
Global $g_Timeout = 0, $g_hMsgBoxOldProc, $g_hMsgBoxBrush, $g_hMsgBoxBtn = 0, $g_bMsgBoxClosing = False, $g_bNCLButtonDown = False, $g_bMsgBoxInitialized = False
Global $g_hMsgBoxSubProc = DllCallbackRegister("_MsgBoxProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
Global $g_pMsgBoxSubProc = DllCallbackGetPtr($g_hMsgBoxSubProc)

Global Enum $PAM_Default, $PAM_AllowDark, $PAM_ForceDark, $PAM_ForceLight, $PAM_Max

;From testing, using $PAM_AllowDark will cause _WinAPI_ShouldAppsUseDarkMode() to follow the actual darkmode setting.
;Otherwise you can force _WinAPI_ShouldAppsUseDarkMode to return either way with $PAM_ForceDark & $PAM_ForceLight.
_WinAPI_SetPreferredAppMode($PAM_ForceDark)

ConsoleWrite(MsgBoxEx("This is a test", "Information", 5) & @CRLF)

Func _WinAPI_SetDlgItemText($hDlg, $nIDDlgItem, $lpString) ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setdlgitemtextw
    Local $aRet = DllCall("user32.dll", "int", "SetDlgItemText", "hwnd", $hDlg, "int", $nIDDlgItem, "str", $lpString)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aRet[0]
EndFunc   ;==>_WinAPI_SetDlgItemText

Func _MsgBoxProc($hWnd, $iMsg, $wParam, $lParam, $iSubClsID, $pData)
    Local Static $hBkColorBrush, $hFooterBrush, $hTimer

    Switch $iMsg

        Case $WM_NCCREATE
            If $g_UseDarkMode Then _WinAPI_DwmSetWindowAttribute($hWnd, $DWMWA_USE_IMMERSIVE_DARK_MODE, True)

        Case $WM_INITDIALOG
            $hBkColorBrush = _WinAPI_CreateSolidBrush($COLOR_BG_DARK)
            $hFooterBrush = _WinAPI_CreateSolidBrush($COLOR_HOTTRACK_MENU)

            _WinAPI_SetDlgItemText($hWnd, $IDOK, StringFormat("%s [%d]",  $g_sBtn1_Txt, $g_Timeout))
            If $g_Timeout Then $hTimer = _Timer_SetTimer($hWnd, 1000, "_TimerProc")

        Case $WM_CTLCOLORSTATIC,  $WM_CTLCOLORDLG
            If $g_UseDarkMode Then
                _WinAPI_SetTextColor($wParam, $COLOR_TEXT_LIGHT)
                _WinAPI_SetBkColor($wParam, $COLOR_BG_DARK)
                Return $hBkColorBrush
            EndIf

        Case $WM_CTLCOLORBTN
            If $g_UseDarkMode Then Return $hBkColorBrush

        Case $WM_PAINT
            If $g_UseDarkMode Then
                Local $tPS = DllStructCreate($tagPAINTSTRUCT)
                Local $hDC = _WinAPI_BeginPaint($hWnd, $tPS)
                Local $tPaintRect = DllStructCreate($tagRECT, DllStructGetPtr($tPS, "rPaint"))
                $tPaintRect.Top = ($tPaintRect.Bottom - 42)
                _WinAPI_FillRect($hDC, $tPaintRect, $hFooterBrush)
                _WinAPI_EndPaint($hWnd, $tPS)
                Return True
            EndIf

        Case $WM_COMMAND
            Local $iNotifCode = _WinAPI_HiWord($wParam)
            Local $iItemId = _WinAPI_LoWord($wParam)
            If (Not $lParam) Or ($iNotifCode = $BN_CLICKED) Then
                Return _Dialog_EndDialog($hWnd, $iItemId)
            EndIf

        Case $WM_DESTROY
            _Timer_KillTimer($hWnd, $hTimer)
            _WinAPI_RemoveWindowSubclass($hWnd, $g_pMsgBoxSubProc, $iSubClsID)
            _WinAPI_SetActiveWindow(_WinAPI_GetParent($hWnd))
            _WinAPI_DeleteObject($hBkColorBrush)
            _WinAPI_DeleteObject($hFooterBrush)

    EndSwitch

    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc

Func _TimerProc($hWnd, $iMsg, $wParam, $lParam)
    If Not _WinAPI_IsWindow($hWnd) Then Return
    If $g_Timeout <= 1 Then _WinAPI_PostMessage($hWnd, $WM_COMMAND, $IDOK + 1, 0)
    $g_Timeout -= 1
    _WinAPI_SetDlgItemText($hWnd, $IDOK + 1, StringFormat("%s [%d]",  $g_sBtn1_Txt, $g_Timeout))
EndFunc

Func _CBTHookProc($nCode, $wParam, $lParam)
    Local Const $hWnd = HWnd($wParam)
    If $nCode < 0 Then Return _WinAPI_CallNextHookEx($g_hMsgBoxHook, $nCode, $wParam, $lParam)

    Switch $nCode
        Case $HCBT_CREATEWND

            Switch _WinAPI_GetClassName($hWnd)
                Case "#32770"
                    _WinAPI_SetWindowSubclass($hWnd, $g_pMsgBoxSubProc, 1000)

                Case "Button"
                    If $g_UseDarkMode Then _WinAPI_SetWindowTheme($hWnd, "DarkMode_Explorer")

            EndSwitch

    EndSwitch
    Return _WinAPI_CallNextHookEx($g_hMsgBoxHook, $nCode, $wParam, $lParam)
EndFunc   ;==>_CBTHookProc

Func MsgBoxEx($sText, $sTitle = Default, $iTimeout = 0, $iFlag = Default, $sBtn_Txt = Default, $hParentHWND = "")
    $g_UseDarkMode = _WinAPI_ShouldAppsUseDarkMode()
    $g_bMsgBoxInitialized = False
    If $sBtn_Txt <> Default Then $g_sBtn1_Txt = $sBtn_Txt
    If $iFlag = Default Then $iFlag = BitOR($MB_TOPMOST, $MB_ICONINFORMATION)
    $g_Timeout = $iTimeout
    Local $hMsgProc = DllCallbackRegister("_CBTHookProc", "int", "uint;wparam;lparam")
    Local Const $hThreadID = _WinAPI_GetCurrentThreadId()
    $g_hMsgBoxHook = _WinAPI_SetWindowsHookEx($WH_CBT, DllCallbackGetPtr($hMsgProc), Null, $hThreadID)
    If $sTitle = Default Then $sTitle = "Information"
    Local Const $iReturn = MsgBox($iFlag, $sTitle, $sText, 0, $hParentHWND)
    If $g_hMsgBoxHook Then _WinAPI_UnhookWindowsHookEx($g_hMsgBoxHook)
    DllCallbackFree($hMsgProc)
    Return $iReturn
EndFunc   ;==>MsgBoxEx

Func _Dialog_EndDialog($hWnd, $iReturn)
    Local $aCall = DllCall("User32.dll", "bool", "EndDialog", "hwnd", $hWnd, "int_ptr", $iReturn)
    If @error Then Return SetError(@error, @extended, False)
    Return $aCall[0]
EndFunc

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

Func _WinAPI_SetPreferredAppMode($iMode)
    Local $aResult = DllCall("UxTheme.dll", "ptr", 135, "int", $iMode)
    If @error Then Return SetError(1, 0, False)
    Return $aResult[0]
EndFunc
Posted
3 hours ago, WildByDesign said:

I really like the technique that you use to paint the darker rectangle around the Edit and ListBox controls to cover up the brighter border. However, I feel like it takes away some of the "depth" of the control and could make the user have a harder time to know which edit/listbox control is active.

Suggestion: When the specific Edit/ListBox control is active, paint the background slightly brighter.

My thought would be to use your existing _WM_CTLCOLOR() function. If we can determine if the Edit/ListBox control has focus (keyboard), change the color of the brush that is returned from that function. This might be the best option since it would work for both Win10 and Win11 users.

I just don't know how to determine if that specific control has the focus. Possibly the control handle can be compared with _WinAPI_GetFocus() but I have not tried.

Alternative:

DarkMode_DarkTheme works perfectly now for Edit controls. Not perfect for ListBox though. But Edit controls are perfect and would not need any additional rectangle painted around them. The only downside is that it would only works for Windows 11 24H2/25H2 with latest updates.

I did some modifications -> 

 

It gets more and more complicated and I'm loosing the overview of all stuff....

 

51 minutes ago, MattyD said:

I had a bit more of a play with that msgbox stuff today and I landed on something like this. It's not meant to be a replacement by any means, it's probably more a scratch pad for my own benefit.  But I figure someone might wish to pick though it.

#AutoIt3Wrapper_UseX64=N

#include <ButtonConstants.au3>
#include <Timers.au3>
#include <WinAPI.au3>
#include <WinAPITheme.au3>
#include <WindowsConstants.au3>
#include <WinAPIGdi.au3>

;Store COLOR_* as coloref (consistancy with _WinAPI_GetSysColor())
Global Const $COLOR_BG_DARK = _WinAPI_SwitchColor(0x202020)
Global Const $COLOR_TEXT_LIGHT = _WinAPI_SwitchColor(0xF0F0F0)
Global Const $COLOR_HOTTRACK_MENU = _WinAPI_SwitchColor(0x3A3A3A)
Global Const $COLOR_TITLE_DARK = _WinAPI_SwitchColor(0x181818)

Global Const $HCBT_MOVESIZE = 0
Global Const $HCBT_MINMAX = 1
Global Const $HCBT_QS = 2
Global Const $HCBT_CREATEWND = 3
Global Const $HCBT_DESTROYWND = 4
Global Const $HCBT_ACTIVATE = 5
Global Const $HCBT_CLICKSKIPPED = 6
Global Const $HCBT_KEYSKIPPED = 7
Global Const $HCBT_SYSCOMMAND = 8
Global Const $HCBT_SETFOCUS = 9

Global $g_hMsgBoxHook, $g_idTImer, $g_sBtn1_Txt = "Close"
Global $g_UseDarkMode
Global $g_Timeout = 0, $g_hMsgBoxOldProc, $g_hMsgBoxBrush, $g_hMsgBoxBtn = 0, $g_bMsgBoxClosing = False, $g_bNCLButtonDown = False, $g_bMsgBoxInitialized = False
Global $g_hMsgBoxSubProc = DllCallbackRegister("_MsgBoxProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
Global $g_pMsgBoxSubProc = DllCallbackGetPtr($g_hMsgBoxSubProc)

Global Enum $PAM_Default, $PAM_AllowDark, $PAM_ForceDark, $PAM_ForceLight, $PAM_Max

;From testing, using $PAM_AllowDark will cause _WinAPI_ShouldAppsUseDarkMode() to follow the actual darkmode setting.
;Otherwise you can force _WinAPI_ShouldAppsUseDarkMode to return either way with $PAM_ForceDark & $PAM_ForceLight.
_WinAPI_SetPreferredAppMode($PAM_ForceDark)

ConsoleWrite(MsgBoxEx("This is a test", "Information", 5) & @CRLF)

Func _WinAPI_SetDlgItemText($hDlg, $nIDDlgItem, $lpString) ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setdlgitemtextw
    Local $aRet = DllCall("user32.dll", "int", "SetDlgItemText", "hwnd", $hDlg, "int", $nIDDlgItem, "str", $lpString)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aRet[0]
EndFunc   ;==>_WinAPI_SetDlgItemText

Func _MsgBoxProc($hWnd, $iMsg, $wParam, $lParam, $iSubClsID, $pData)
    Local Static $hBkColorBrush, $hFooterBrush, $hTimer

    Switch $iMsg

        Case $WM_NCCREATE
            If $g_UseDarkMode Then _WinAPI_DwmSetWindowAttribute($hWnd, $DWMWA_USE_IMMERSIVE_DARK_MODE, True)

        Case $WM_INITDIALOG
            $hBkColorBrush = _WinAPI_CreateSolidBrush($COLOR_BG_DARK)
            $hFooterBrush = _WinAPI_CreateSolidBrush($COLOR_HOTTRACK_MENU)

            _WinAPI_SetDlgItemText($hWnd, $IDOK, StringFormat("%s [%d]",  $g_sBtn1_Txt, $g_Timeout))
            If $g_Timeout Then $hTimer = _Timer_SetTimer($hWnd, 1000, "_TimerProc")

        Case $WM_CTLCOLORSTATIC,  $WM_CTLCOLORDLG
            If $g_UseDarkMode Then
                _WinAPI_SetTextColor($wParam, $COLOR_TEXT_LIGHT)
                _WinAPI_SetBkColor($wParam, $COLOR_BG_DARK)
                Return $hBkColorBrush
            EndIf

        Case $WM_CTLCOLORBTN
            If $g_UseDarkMode Then Return $hBkColorBrush

        Case $WM_PAINT
            If $g_UseDarkMode Then
                Local $tPS = DllStructCreate($tagPAINTSTRUCT)
                Local $hDC = _WinAPI_BeginPaint($hWnd, $tPS)
                Local $tPaintRect = DllStructCreate($tagRECT, DllStructGetPtr($tPS, "rPaint"))
                $tPaintRect.Top = ($tPaintRect.Bottom - 42)
                _WinAPI_FillRect($hDC, $tPaintRect, $hFooterBrush)
                _WinAPI_EndPaint($hWnd, $tPS)
                Return True
            EndIf

        Case $WM_COMMAND
            Local $iNotifCode = _WinAPI_HiWord($wParam)
            Local $iItemId = _WinAPI_LoWord($wParam)
            If (Not $lParam) Or ($iNotifCode = $BN_CLICKED) Then
                Return _Dialog_EndDialog($hWnd, $iItemId)
            EndIf

        Case $WM_DESTROY
            _Timer_KillTimer($hWnd, $hTimer)
            _WinAPI_RemoveWindowSubclass($hWnd, $g_pMsgBoxSubProc, $iSubClsID)
            _WinAPI_SetActiveWindow(_WinAPI_GetParent($hWnd))
            _WinAPI_DeleteObject($hBkColorBrush)
            _WinAPI_DeleteObject($hFooterBrush)

    EndSwitch

    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc

Func _TimerProc($hWnd, $iMsg, $wParam, $lParam)
    If Not _WinAPI_IsWindow($hWnd) Then Return
    If $g_Timeout <= 1 Then _WinAPI_PostMessage($hWnd, $WM_COMMAND, $IDOK + 1, 0)
    $g_Timeout -= 1
    _WinAPI_SetDlgItemText($hWnd, $IDOK + 1, StringFormat("%s [%d]",  $g_sBtn1_Txt, $g_Timeout))
EndFunc

Func _CBTHookProc($nCode, $wParam, $lParam)
    Local Const $hWnd = HWnd($wParam)
    If $nCode < 0 Then Return _WinAPI_CallNextHookEx($g_hMsgBoxHook, $nCode, $wParam, $lParam)

    Switch $nCode
        Case $HCBT_CREATEWND

            Switch _WinAPI_GetClassName($hWnd)
                Case "#32770"
                    _WinAPI_SetWindowSubclass($hWnd, $g_pMsgBoxSubProc, 1000)

                Case "Button"
                    If $g_UseDarkMode Then _WinAPI_SetWindowTheme($hWnd, "DarkMode_Explorer")

            EndSwitch

    EndSwitch
    Return _WinAPI_CallNextHookEx($g_hMsgBoxHook, $nCode, $wParam, $lParam)
EndFunc   ;==>_CBTHookProc

Func MsgBoxEx($sText, $sTitle = Default, $iTimeout = 0, $iFlag = Default, $sBtn_Txt = Default, $hParentHWND = "")
    $g_UseDarkMode = _WinAPI_ShouldAppsUseDarkMode()
    $g_bMsgBoxInitialized = False
    If $sBtn_Txt <> Default Then $g_sBtn1_Txt = $sBtn_Txt
    If $iFlag = Default Then $iFlag = BitOR($MB_TOPMOST, $MB_ICONINFORMATION)
    $g_Timeout = $iTimeout
    Local $hMsgProc = DllCallbackRegister("_CBTHookProc", "int", "uint;wparam;lparam")
    Local Const $hThreadID = _WinAPI_GetCurrentThreadId()
    $g_hMsgBoxHook = _WinAPI_SetWindowsHookEx($WH_CBT, DllCallbackGetPtr($hMsgProc), Null, $hThreadID)
    If $sTitle = Default Then $sTitle = "Information"
    Local Const $iReturn = MsgBox($iFlag, $sTitle, $sText, 0, $hParentHWND)
    If $g_hMsgBoxHook Then _WinAPI_UnhookWindowsHookEx($g_hMsgBoxHook)
    DllCallbackFree($hMsgProc)
    Return $iReturn
EndFunc   ;==>MsgBoxEx

Func _Dialog_EndDialog($hWnd, $iReturn)
    Local $aCall = DllCall("User32.dll", "bool", "EndDialog", "hwnd", $hWnd, "int_ptr", $iReturn)
    If @error Then Return SetError(@error, @extended, False)
    Return $aCall[0]
EndFunc

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

Func _WinAPI_SetPreferredAppMode($iMode)
    Local $aResult = DllCall("UxTheme.dll", "ptr", 135, "int", $iMode)
    If @error Then Return SetError(1, 0, False)
    Return $aResult[0]
EndFunc

 

Very good - seems to be more stable and the behaviour is same as MsgBox() - thanks.

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)
21 hours ago, MattyD said:

I had a bit more of a play with that msgbox stuff today and I landed on something like this. It's not meant to be a replacement by any means, it's probably more a scratch pad for my own benefit.  But I figure someone might wish to pick though it.

Hey Matty. So this MsgBoxEx from you is quite incredible. I have extended it by allowing application of Win11 materials (Mica, Tabbed, Acrylic, etc.) and DPI scaling for certain measurements where it was needed (eg. footer height painting). The fact that your MsgBoxEx follows standard MsgBox return values and everything I feel like you've got something special here.

Msg-Box-Ex.png

I do not want to take away from the current thread/topic. Would you be willing to open a new topic for your MsgBoxEx script?

I feel like it has such value that it deserves it. But also, I have some questions, suggestions and possibly some code to share back to improve it. And I feel like if it has its own topic, it would be easy for you to update/modify that first post to always have the latest. By the way, thank you for all of the incredible things that you share with the community. This is yet another gem. :)

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
×
×
  • Create New...