WildByDesign Posted February 27 Posted February 27 In a Dark Theme UDF that I have been working on recently, one of the remaining controls left to receive any kind of dark mode treatment is SysDateTimePick32. Subclassing methods in other programming languages are scarce and it seems to be not easy to subclass properly. So I am trying to think outside the box here and think of other methods. Example screenshot of current GUIDarkTheme UDF used on SampleControls.au3: Spoiler I have thought about some things such as setting the SysDateTimePick32 control as a layer and setting a transparent color which can work, but leaves the text very grainy. I was thinking about _WinAPI_InvertRgn most recently but not sure how exactly to use it. There aren't any previous examples in the forum either. I have stripped down a copy of SampleControls.au3 and got rid of most of the controls to help isolate SysDateTimePick32 if anyone wants to give it a try. If _WinAPI_InvertRgn doesn't give any desired results, possibly we can try other ideas here in this thread. expandcollapse popup; AutoIt GUI Example ; Created: 17/01/2005 - CyberSlug ; Modifed: 05/12/2011 - guinness ; Modifed: 09/06/2014 - mLipok ; Modifed: 15/10/2018 - mLipok #Region INCLUDE #include <GuiConstantsEx.au3> #EndRegion INCLUDE ; set working directory to the original SampleControls example which contains the necessary resources FileChangeDir(StringLeft(@AutoItExe, StringInStr(@AutoItExe, "\", 0, -1) -1) & "\Examples\GUI") #Region INITIALIZATION and EXIT _Example() Exit ; Finished! #EndRegion INITIALIZATION and EXIT Func _Example() #Region GUI Local $hGUI = GUICreate("Sample GUI", 400, 440) GUISetBkColor(0x202020) #EndRegion GUI #Region DATE GUICtrlCreateDate("", 5, 280, 200, 20) #EndRegion DATE #Region BUTTON Local $idButton = GUICtrlCreateButton("Sample Button", 10, 330, 100, 30) GUICtrlSetTip(-1, '#Region BUTTON') #EndRegion BUTTON #Region GUI MESSAGE LOOP ControlFocus($hGUI, "", $idButton) GUISetState(@SW_SHOW) While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd GUIDelete() #EndRegion GUI MESSAGE LOOP EndFunc ;==>_Example
Werty Posted February 27 Posted February 27 Rgn is usually _WinAPI_CreateRectRgn() (or CreateRoundRectRgn), but somehow SysDateTimePick32 doesnt want to play along, also behaves weird like it's drawn from bottom up, as in X,Y pos is not the top left corner but the lower left corner. Rgn works fine with SetWindowRgn but not with InvertRgn... #Region DATE $hRgn = _WinAPI_CreateRectRgn(5, 300, 210, 330) _WinAPI_SetWindowRgn($hGui, $hRgn) ;~ _WinAPI_InvertRgn ($hGUI, $hRgn) ; <-- doesnt want to play along for some reason GUICtrlCreateDate("", 5, 280, 200, 20) #EndRegion DATE WildByDesign 1 Some guy's script + some other guy's script = my script!
WildByDesign Posted February 27 Author Posted February 27 Thanks @Werty. I still haven't experimented or learned much about creating regions, so even though _WinAPI_InvertRgn may not end up successful in this, at least I end up learning more about Rgn. I appreciate your help. From my understanding, _WinAPI_InvertRgn is asking for a handle to the DC, not the GUI handle. But what I don't know is whether it is supposed to be the device context for the GUI or the device context for the date control. There is also _WinAPI_InvertRect which also wants a DC but goes by RECT instead of Rgn. I tried testing it briefly but since I don't understand it very well at all I've probably done it all wrong. Here is my current testing mess: expandcollapse popup; AutoIt GUI Example ; Created: 17/01/2005 - CyberSlug ; Modifed: 05/12/2011 - guinness ; Modifed: 09/06/2014 - mLipok ; Modifed: 15/10/2018 - mLipok #Region INCLUDE #include <GuiConstantsEx.au3> #include <WinAPIGdi.au3> #EndRegion INCLUDE ; set working directory to the original SampleControls example which contains the necessary resources FileChangeDir(StringLeft(@AutoItExe, StringInStr(@AutoItExe, "\", 0, -1) -1) & "\Examples\GUI") #Region INITIALIZATION and EXIT _Example() Exit ; Finished! #EndRegion INITIALIZATION and EXIT Func _Example() #Region GUI Local $hGUI = GUICreate("Sample GUI", 400, 440) GUISetBkColor(0x202020) #EndRegion GUI #Region DATE Local $idDate = GUICtrlCreateDate("", 5, 280, 200, 20) #EndRegion DATE #Region BUTTON Local $idButton = GUICtrlCreateButton("Sample Button", 10, 330, 100, 30) GUICtrlSetTip(-1, '#Region BUTTON') #EndRegion BUTTON #Region GUI MESSAGE LOOP ControlFocus($hGUI, "", $idButton) GUISetState(@SW_SHOW) Local $aCtrl_pos = ControlGetPos($hGUI, "", $idDate) Local $hDC = _WinAPI_GetDC(GUICtrlGetHandle($idDate)) ;Local $tRECT = _WinAPI_CreateRect(0, 0, 0, 0) ;$tRECT.Left = $aCtrl_pos[0] ;$tRECT.Top = $aCtrl_pos[1] ;$tRECT.Right = $aCtrl_pos[0] ;$tRECT.Bottom = $aCtrl_pos[1] ;_WinAPI_InvertRect($hDC, $tRECT) ;$hRgn = _WinAPI_CreateRectRgn($aCtrl_pos[0], $aCtrl_pos[1], $aCtrl_pos[0] + $aCtrl_pos[2], $aCtrl_pos[1] + $aCtrl_pos[3]) $hRgn = _WinAPI_CreateRectRgn(5, 300, 210, 330) _WinAPI_SetWindowRgn($hGUI, $hRgn) _WinAPI_InvertRgn($hDC, $hRgn) ; <-- doesnt want to play along for some reason ;_WinAPI_DeleteObject($hRgn) While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd GUIDelete() #EndRegion GUI MESSAGE LOOP EndFunc ;==>_Example
Nine Posted February 27 Posted February 27 Not a bad idea Wild. Here my take on it : expandcollapse popup; From Nine #include <WindowsConstants.au3> #include <GuiConstants.au3> #include <GuiDateTimePicker.au3> #include <WinAPI.au3> Opt("MustDeclareVars", True) Example() Func Example() Local $hGUI = GUICreate("Sample GUI", 400, 440) GUISetBkColor(0x202020) Local $idDate = GUICtrlCreateDate("", 15, 15, 200, 20) GUISetState() InvertDateColor($hGUI, $idDate) While True Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd EndFunc ;==>Example Func InvertDateColor($hWnd, $idCtrl) Local $aPos = ControlGetPos($hWnd, "", $idCtrl) Local $tPoint1 = DllStructCreate($tagPOINT), $tPoint2 = DllStructCreate($tagPOINT) $tPoint1.X = $aPos[0] $tPoint1.Y = $aPos[1] _WinAPI_ClientToScreen($hWnd, $tPoint1) $tPoint2.X = $aPos[0] + $aPos[2] $tPoint2.Y = $aPos[1] + $aPos[3] _WinAPI_ClientToScreen($hWnd, $tPoint2) $aPos = WinGetPos($hWnd) Local $tRect = DllStructCreate($tagRECT) $tRect.left = $tPoint1.X - $aPos[0] $tRect.top = $tPoint1.Y - $aPos[1] $tRect.right = $tPoint2.X - $aPos[0] $tRect.bottom = $tPoint2.Y - $aPos[1] Local $hDC = _WinAPI_GetWindowDC($hWnd) _WinAPI_InvertRect($hDC, $tRect) EndFunc ;==>InvertDateColor We will need to intercept all events that could change the color. Have to think of the best way now... WildByDesign 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
WildByDesign Posted February 27 Author Posted February 27 39 minutes ago, Nine said: Not a bad idea Wild. Here my take on it : Thanks. I often have decent ideas or at least the determination to never give up on an idea. Thank you for helping to bring this idea to life. I really had no idea whether it would work or not. Here is what it looks like when I give the focus (prior to GUISetState) to a button: I think it looks better than expected. That is with high DPI as well. 41 minutes ago, Nine said: We will need to intercept all events that could change the color. Have to think of the best way now... We would have to intercept anytime the cursor hovers over it and likely click events as well. If you are able to expand on this part, I would really appreciate it. There is a lot of potential with this, I think.
Nine Posted February 27 Posted February 27 57 minutes ago, WildByDesign said: If you are able to expand on this part I tested different subclassing methods and they were not very conclusive. Best approach I found so far is this one. Not perfect but maybe good enough. We could test 2 regions though, the text part, and the dropdown part. I'll let you play with it for now... expandcollapse popup; From Nine #include <WindowsConstants.au3> #include <GuiConstants.au3> #include <GuiDateTimePicker.au3> #include <WinAPI.au3> Opt("MustDeclareVars", True) Example() Func Example() Local $hGUI = GUICreate("Sample GUI", 400, 440) GUISetBkColor(0x202020) Local $idDate = GUICtrlCreateDate("", 15, 15, 200, 20) GUISetState() InvertDateColor($hGUI, $idDate) _WinAPI_SetTimer($hGUI, $idDate, 10, 0) GUIRegisterMsg($WM_TIMER, WM_TIMER) While True Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd _WinAPI_KillTimer($hGUI, $idDate) EndFunc ;==>Example Func InvertDateColor($hWnd, $idCtrl) Local Static $nColor Local $aPos = ControlGetPos($hWnd, "", $idCtrl) If @error Then Return Local $tPoint1 = DllStructCreate($tagPOINT), $tPoint2 = DllStructCreate($tagPOINT) $tPoint1.X = $aPos[0] $tPoint1.Y = $aPos[1] _WinAPI_ClientToScreen($hWnd, $tPoint1) $tPoint2.X = $aPos[0] + $aPos[2] $tPoint2.Y = $aPos[1] + $aPos[3] _WinAPI_ClientToScreen($hWnd, $tPoint2) $aPos = WinGetPos($hWnd) Local $tRect = DllStructCreate($tagRECT) $tRect.left = $tPoint1.X - $aPos[0] $tRect.top = $tPoint1.Y - $aPos[1] $tRect.right = $tPoint2.X - $aPos[0] $tRect.bottom = $tPoint2.Y - $aPos[1] Local $hDC = _WinAPI_GetWindowDC($hWnd) If $nColor = _WinAPI_GetPixel($hDC, $tRect.left + 2, $tRect.top + 2) Then Return _WinAPI_InvertRect($hDC, $tRect) $nColor = _WinAPI_GetPixel($hDC, $tRect.left + 2, $tRect.top + 2) EndFunc ;==>InvertDateColor Func WM_TIMER($hWnd, $iMsg, $wParam, $lParam) InvertDateColor($hWnd, Int($wParam)) EndFunc ;==>WM_TIMER WildByDesign 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
Solution UEZ Posted February 27 Solution Posted February 27 (edited) expandcollapse popup;Coded by UEZ build 2026-02-28 beta #include <WindowsConstants.au3> #include <GuiConstants.au3> #include <GuiDateTimePicker.au3> #include <GuiMonthCal.au3> #include <WinAPI.au3> #include <WinAPISysWin.au3> #include <WinAPITheme.au3> Opt("MustDeclareVars", True) Global $g_hDateProc_CB, $g_pDateProc_CB, $g_hDateOldProc, $g_hDate Global Const $PRF_CLIENT = 0x0004, $COLOR_BORDER = 0x404040, $COLOR_BG_DARK = 0x202020, $COLOR_TEXT_LIGHT = 0xFFFFFF, $COLOR_BORDER_LIGHT = 0xD8D8D8 Global $g_bHover = False Example() Func Example() Local $hGUI = GUICreate("Sample GUI", 400, 240) GUISetBkColor($COLOR_BG_DARK) Local $idDate = GUICtrlCreateDate("", 15, 15, 200, 20) $g_hDate = GUICtrlGetHandle($idDate) $g_hDateProc_CB = DllCallbackRegister('_DateProc', 'ptr', 'hwnd;uint;wparam;lparam') $g_pDateProc_CB = DllCallbackGetPtr($g_hDateProc_CB) $g_hDateOldProc = _WinAPI_SetWindowLong($g_hDate, $GWL_WNDPROC, $g_pDateProc_CB) GUIRegisterMsg($WM_NOTIFY, "_WM_NOTIFY") GUISetState() While True Switch GUIGetMsg() Case $GUI_EVENT_CLOSE _WinAPI_SetWindowLong($g_hDate, $GWL_WNDPROC, $g_hDateOldProc) DllCallbackFree($g_hDateProc_CB) GUIRegisterMsg($WM_NOTIFY, "") GUIDelete() ExitLoop EndSwitch WEnd EndFunc ;==>Example Func _DateProc($hWnd, $iMsg, $wParam, $lParam) Switch $iMsg Case $WM_PAINT Local $tPaint = DllStructCreate($tagPAINTSTRUCT) Local $hDC = _WinAPI_BeginPaint($hWnd, $tPaint) Local $tClient = _WinAPI_GetClientRect($hWnd) Local $iW = $tClient.Right Local $iH = $tClient.Bottom ; --- Memory DC for flicker-free rendering --- Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC) Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iW, $iH) Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap) ; 1. Let Windows draw the light-mode control into memory DC _WinAPI_CallWindowProc($g_hDateOldProc, $hWnd, $WM_PRINTCLIENT, $hMemDC, $PRF_CLIENT) ; 2. Invert all pixels (background becomes black, text white, selection orange) Local $tRect = DllStructCreate($tagRECT) $tRect.right = $iW $tRect.bottom = $iH _WinAPI_InvertRect($hMemDC, $tRect) ; --- 3. PIXEL HACK: destroy orange highlight & set background color --- Local $iSize = $iW * $iH Local $tPixels = DllStructCreate("dword c[" & $iSize & "]") ; Load pixel array directly from bitmap memory Local $iBytes = DllCall("gdi32.dll", "long", "GetBitmapBits", "handle", $hBitmap, "long", $iSize * 4, "ptr", DllStructGetPtr($tPixels))[0] If $iBytes = $iSize * 4 Then Local $iPixel, $r, $g, $b, $iGray For $i = 1 To $iSize $iPixel = $tPixels.c(($i)) ; Split into color channels $b = BitAND($iPixel, 0xFF) $g = BitAND(BitShift($iPixel, 8), 0xFF) $r = BitAND(BitShift($iPixel, 16), 0xFF) ; Convert to grayscale (orange becomes mid-gray) $iGray = Int(($r + $g + $b) / 3) ; Very dark pixel = inverted white background If $iGray < 15 Then $iPixel = $COLOR_BG_DARK ; Replace with exact GUI background color Else ; Grayscale value for text (white) and selection (gray) ; (negative BitShift shifts left in AutoIt) $iPixel = BitOR(BitShift($iGray, -16), BitShift($iGray, -8), $iGray) EndIf $tPixels.c(($i)) = $iPixel Next ; Write cleaned pixels back into the bitmap DllCall("gdi32.dll", "long", "SetBitmapBits", "handle", $hBitmap, "long", $iSize * 4, "ptr", DllStructGetPtr($tPixels)) EndIf ; --- END PIXEL HACK --- ; --- Border color (hover effect) --- Local $iBorderColor = $COLOR_BORDER If _WinAPI_GetFocus() = $hWnd Then $iBorderColor = $COLOR_BORDER Local $tCursorPos = DllStructCreate($tagPOINT) DllCall("user32.dll", "bool", "GetCursorPos", "struct*", $tCursorPos) DllCall("user32.dll", "bool", "ScreenToClient", "hwnd", $hWnd, "struct*", $tCursorPos) If $tCursorPos.X >= 0 And $tCursorPos.X <= $iW And $tCursorPos.Y >= 0 And $tCursorPos.Y <= $iH Then $iBorderColor = $COLOR_BORDER_LIGHT EndIf ; --- Draw border --- Local $hPen = _WinAPI_CreatePen(0, 1, _ColorToCOLORREF($iBorderColor)) Local $hNullBr = _WinAPI_GetStockObject(5) Local $hOldPen = _WinAPI_SelectObject($hMemDC, $hPen) Local $hOldBr = _WinAPI_SelectObject($hMemDC, $hNullBr) DllCall("gdi32.dll", "bool", "Rectangle", "handle", $hMemDC, "int", 0, "int", 0, "int", $iW, "int", $iH) _WinAPI_SelectObject($hMemDC, $hOldPen) _WinAPI_SelectObject($hMemDC, $hOldBr) _WinAPI_DeleteObject($hPen) ; --- Copy finished result to screen in one step (no flicker) --- _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hMemDC, 0, 0, $SRCCOPY) ; --- Cleanup --- _WinAPI_SelectObject($hMemDC, $hOldBmp) _WinAPI_DeleteObject($hBitmap) _WinAPI_DeleteDC($hMemDC) _WinAPI_EndPaint($hWnd, $tPaint) Return 0 Case $WM_ERASEBKGND Return 1 Case $WM_SETFOCUS, $WM_KILLFOCUS, $WM_LBUTTONDOWN, $WM_LBUTTONUP Local $iRet = _WinAPI_CallWindowProc($g_hDateOldProc, $hWnd, $iMsg, $wParam, $lParam) _WinAPI_InvalidateRect($hWnd, 0, False) Return $iRet Case $WM_MOUSEMOVE Local $iRet = _WinAPI_CallWindowProc($g_hDateOldProc, $hWnd, $iMsg, $wParam, $lParam) If Not $g_bHover Then $g_bHover = True _WinAPI_InvalidateRect($hWnd, 0, False) EndIf Return $iRet Case $WM_MOUSELEAVE $g_bHover = False _WinAPI_InvalidateRect($hWnd, 0, False) EndSwitch Return _WinAPI_CallWindowProc($g_hDateOldProc, $hWnd, $iMsg, $wParam, $lParam) EndFunc ;==>_DateProc 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 _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 visual theme when DateTime control receives focus _WinAPI_SetThemeAppProperties(0) Case $DTN_DROPDOWN ; Apply dark colors when the calendar dropdown opens _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 dropdown closed - no action needed EndSwitch EndSwitch Return $GUI_RUNDEFMSG EndFunc ;==>_WM_NOTIFY Edited February 27 by UEZ WildByDesign and argumentum 2 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ
WildByDesign Posted February 27 Author Posted February 27 @UEZ That is truly impressive. I was looking into ways to get rid of the orange highlight and you had figured it out pretty quickly. Adding high DPI scaling to this, it looks absolutely production ready. And you retain all of the functionality of the control. I was looking into hacks that would have taken away focus from the control and we would have lost the ability to use the keyboard to navigate the control. But you nailed it perfectly. Now I understand your message of "Never say never ". Excellent work!
WildByDesign Posted February 28 Author Posted February 28 I have a feeling that this _WinAPI_InvertRect technique might actually produce better results for statusbar in comparison to the actual dark theme. For example, the following lines work successfully to enable native dark mode theme on a statusbar control: ; msctls_statusbar32 _WinAPI_SetWindowTheme($hWnd, "DarkMode_DarkTheme", "Status") ; Win11 24H2/25H2 _WinAPI_SetWindowTheme($hWnd, "DarkMode", "ExplorerStatusBar") They work quite well but lack dark theme for the size grip ($SBS_SIZEBOX or $SBS_SIZEGRIP). But a straight up color invert of the light mode statusbar actually looks even better compared to the actual dark-themed variant. I will have to look into this when I get some more time.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now