Jump to content

Recommended Posts

Posted (edited)

This is something that I need for the GUIDarkTheme UDF project, but would be beneficial in general for anyone using dark mode in their GUI.

For Windows 11 users, there is no problem because you can use _WinAPI_SetWindowTheme($h_UpDown, "DarkMode_Explorer") on the UpDown control handle.

Problem: This does not work for Windows 10 users because DarkMode_Explorer pulls the resources from DarkMode_Explorer::Spin in the msstyles theme file and those specific resources do not exist for Windows 10 users.

We would have to do subclassing to achieve dark mode on UpDown controls for Windows 10 users and this is where I need some help. If you have some time and have to skills to do this, I would really appreciate it. Please and thank you. :)

There are some good examples for subclassing UpDown controls in C++ in the win32-darkmodelib library:

Link: win32-darkmodelib/src/DmlibSubclassControl.cpp at main · ozone10/win32-darkmodelib

Link: win32-darkmodelib/src/Darkmodelib.cpp at main · ozone10/win32-darkmodelib

I've made an example to get things started:

DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", @AutoItX64 ? "int64" : "int", -2)

#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>
#include <WindowsStylesConstants.au3>
#include <WinAPITheme.au3>
#include <WinAPISysWin.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPIGdiDC.au3>
#include <APIGdiConstants.au3>

Example()

Func Example()
    GUICreate("Dark Mode UpDown", 400, 300)
    GUISetBkColor(0x323232)
    GUISetFont(14)

    ; Apply dark mode to
    GUIRegisterMsg($WM_CTLCOLOREDIT, "_WM_CTLCOLOR")

    Local $idInput = GUICtrlCreateInput("2", 100, 100, 50, 20, -1, $WS_EX_STATICEDGE)
    Local $h_Input = GUICtrlGetHandle($idInput)
    Local $idUpDown = GUICtrlCreateUpdown($idInput)
    Local $h_UpDown = GUICtrlGetHandle($idUpDown)

    ; Resize input control
    GUICtrlSetPos($idInput, 150, 100, 100, 40)

    If @OSBuild >= 22000 Then
        ; Apply dark mode to UpDown (spinner) control on Windows 11
        _WinAPI_SetWindowTheme($h_UpDown, "DarkMode_Explorer") ; pulls resources from DarkMode_Explorer::Spin in theme file
                                                               ; Problem: Windows 10 does not have DarkMode_Explorer::Spin
    EndIf

    If @OSBuild > 10240 And @OSBuild <= 19045 Then
        ; Apply dark mode to UpDown (spinner) control on Windows 10
        ; TODO: This would have to be achieved with subclassing
        ; C++ subclassing examples:
        ;       https://github.com/ozone10/win32-darkmodelib/blob/main/src/DmlibSubclassControl.cpp#L1020
        ;       https://github.com/ozone10/win32-darkmodelib/blob/main/src/Darkmodelib.cpp#L1168
    EndIf

    GUISetState(@SW_SHOW)

    Local $idMsg
    ; Loop until the user exits.
    While 1
        $idMsg = GUIGetMsg()

        Switch $idMsg
            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd
EndFunc   ;==>Example

Func _WM_CTLCOLOR($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg
    Local $hDC = $wParam
    Local $hCtrl = $lParam
    _WinAPI_SetTextColor($hDC, 0xFFFFFF)
    Local $hBrush = _WinAPI_CreateSolidBrush(0x202020)
    _WinAPI_SetBkColor($hDC, 0x202020)
    _WinAPI_SetBkMode($hDC, $TRANSPARENT)
    Return $hBrush
EndFunc   ;==>_WM_CTLCOLOR

 

Edited by WildByDesign
Posted

Here something to get you started :

; From Nine
#include <GUIConstants.au3>
#include <WinAPI.au3>
#include <WindowsNotifsConstants.au3>

Global Const $UDM_GETBUDDY = 1130

Example()

Func Example()
  GUICreate("UpDown")

  Local $idInput = GUICtrlCreateInput("0", 10, 10, 50, 20)
  Local $idUpDown = GUICtrlCreateUpdown($idInput)
  Local $hUpDown = GUICtrlGetHandle($idUpDown)

  Local $hDll = DllCallbackRegister(UpDownSub, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
  _WinAPI_SetWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
  GUISetState()

  Do
  Until GUIGetMsg() = $GUI_EVENT_CLOSE

  _WinAPI_RemoveWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
  DllCallbackFree($hDll)
EndFunc   ;==>Example

Func UpDownSub($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
  Switch $iMsg
    Case $WM_PAINT
      Local $tPaint, $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)
      Local $tRect = _WinAPI_GetClientRect($hWnd)

      Local $hBrush = _WinAPI_CreateSolidBrush(0x808080)
      _WinAPI_SetBkMode($hDC, $TRANSPARENT)
      _WinAPI_SelectObject($hDC, $hBrush)
      _WinAPI_Rectangle($hDC, $tRect)

      _WinAPI_SetTextColor($hDC, 0xFFFFFF)
      Local $hFont = _WinAPI_CreateFont(8, 6)
      _WinAPI_SelectObject($hDC, $hFont)
      $tRect.top += 2
      _WinAPI_DrawText($hDC, "▲" & @LF & "▼", $tRect, $DT_CENTER)

      _WinAPI_DeleteObject($hBrush)
      _WinAPI_DeleteObject($hFont)
      _WinAPI_EndPaint($hWnd, $tPaint)
    Case $WM_LBUTTONDOWN
      Local $hBuddy = HWnd(_SendMessage($hWnd, $UDM_GETBUDDY))
      Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
      ControlSetText("", "", $hBuddy, ControlGetText("", "", $hBuddy) + ($iPos ? -1 : +1))
      _SendMessage($hBuddy, $EM_SETSEL, 0, -1)
      Return
    Case $WM_LBUTTONUP, $WM_MOUSEMOVE
      _WinAPI_InvalidateRect($hWnd, 0, False)
      Return
  EndSwitch
  Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>UpDownSub

That should work with any OS...

Posted (edited)
9 hours ago, Nine said:

Here something to get you started :

Thank you so much. I've been playing around with this example off and on throughout the day trying various things. I appreciate that you took time to help me out with this. Your example works great, as always.

I tried a few times through the day to extend this in various ways but failed spectacularly on all attempts. I tried, for example:

  • Split the rectangle into two separate rectangles based on the height of the control (since height could vary)
  • Adding indication of hovering the cursor over the rectangle(s) to show different color

Usually I am pretty good at figuring things out, but UpDown controls are peculiar.

I like your idea of using fonts to draw the arrows. I was hoping to split the rectangle into two rectangles (buttons) and draw the arrow in each, horizontally and vertically centered, since from the perspective of the UDF these UpDown controls could vary in height.

I was really hoping that I could make some progress on my own before reaching out for more help. Sorry about that.

Edited by WildByDesign
Posted

Here a tad more elaborated :

; From Nine
#include <GUIConstants.au3>
#include <WinAPI.au3>
#include <WindowsNotifsConstants.au3>

Global Const $UDM_GETBUDDY = 1130

Example()

Func Example()
  GUICreate("UpDown")

  Local $idInput = GUICtrlCreateInput("0", 10, 10, 60, 25)
  Local $idUpDown = GUICtrlCreateUpdown($idInput)
  Local $hUpDown = GUICtrlGetHandle($idUpDown)

  Local $hDll = DllCallbackRegister(UpDownSub, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
  _WinAPI_SetWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
  GUISetState()

  Do
  Until GUIGetMsg() = $GUI_EVENT_CLOSE

  _WinAPI_RemoveWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
  DllCallbackFree($hDll)
EndFunc   ;==>Example

Func UpDownSub($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
  Local Static $bHover
  Switch $iMsg
    Case $WM_PAINT
      Local $tPaint, $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)
      Local $tRect = _WinAPI_GetClientRect($hWnd)

      Local $hPen = _WinAPI_CreatePen($PS_SOLID, 1, 0)
      Local $hBrush = _WinAPI_CreateSolidBrush(0x808080)
      _WinAPI_SetBkMode($hDC, $TRANSPARENT)
      _WinAPI_SelectObject($hDC, $hBrush)
      _WinAPI_Rectangle($hDC, $tRect)
      _WinAPI_DrawLine($hDC, 0, Int($tRect.bottom / 2), $tRect.right, Int($tRect.bottom / 2))
      _WinAPI_DeleteObject($hPen)
      _WinAPI_DeleteObject($hBrush)

      If $bHover Then
        Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
        $hBrush = _WinAPI_CreateSolidBrush(0xA0A0A0)
        $tRectTmp = _WinAPI_GetClientRect($hWnd)
        If $iPos Then
          $tRectTmp.top = Int($tRect.bottom / 2)
        Else
          $tRectTmp.bottom = Int($tRect.bottom / 2)
        EndIf
        _WinAPI_SelectObject($hDC, $hBrush)
        _WinAPI_Rectangle($hDC, $tRectTmp)
        _WinAPI_DeleteObject($hBrush)
      EndIf

      _WinAPI_SetTextColor($hDC, 0xFFFFFF)
      Local $hFont = _WinAPI_CreateFont(Int($tRect.bottom / 3), Int($tRect.right / 3))
      _WinAPI_SelectObject($hDC, $hFont)
      $tRect.top += 2
      _WinAPI_DrawText($hDC, "▲", $tRect, $DT_CENTER)
      $tRect.top += Int($tRect.bottom / 2)
      _WinAPI_DrawText($hDC, "▼", $tRect, $DT_CENTER)

      _WinAPI_DeleteObject($hFont)
      _WinAPI_EndPaint($hWnd, $tPaint)
    Case $WM_LBUTTONDOWN
      Local $hBuddy = HWnd(_SendMessage($hWnd, $UDM_GETBUDDY))
      Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
      ControlSetText("", "", $hBuddy, ControlGetText("", "", $hBuddy) + ($iPos ? -1 : +1))
      _SendMessage($hBuddy, $EM_SETSEL, 0, -1)
      Return
    Case $WM_LBUTTONUP
      _WinAPI_InvalidateRect($hWnd, 0, False)
      Return
    Case $WM_MOUSEMOVE
      $bHover = True
      _WinAPI_TrackMouseEvent($hWnd, $TME_LEAVE)
      _WinAPI_InvalidateRect($hWnd, 0, False)
      Return
    Case $WM_MOUSELEAVE
      $bHover = False
      _WinAPI_InvalidateRect($hWnd, 0, False)
      Return
  EndSwitch
  Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>UpDownSub

 

Posted (edited)
1 hour ago, Nine said:

Here a tad more elaborated :

This is fantastic. Works very well and definitely something that I can work with and hopefully expand some more. Thank you. :)

Here is what I have done so far:

  • Made everything more dark mode
  • Moved the UpDown control to the right by 2 pixels to prevent clipping by Edit control (Windows issue with UpDown controls by default)

Question: Do you know if anything can be done about the flickering that sometimes (not always) happens when hovering over the UpDown buttons?

My current testing example:

DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", @AutoItX64 ? "int64" : "int", -2)

#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>
#include <WindowsStylesConstants.au3>
#include <WinAPITheme.au3>
#include <WinAPISysWin.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPIGdiDC.au3>
#include <APIGdiConstants.au3>
#include <GUIConstants.au3>

#include <WinAPI.au3>
#include <Array.au3>

Global Const $UDM_GETBUDDY = 1130

Example()

Func Example()
    Local $hGUI = GUICreate("Dark Mode UpDown", 400, 300)
    GUISetBkColor(0x323232)
    GUISetFont(14)

    ; Apply dark mode to Edit box
    GUIRegisterMsg($WM_CTLCOLOREDIT, "_WM_CTLCOLOR")

    Local $idInput = GUICtrlCreateInput("2", 100, 100, 50, 20, -1, $WS_EX_STATICEDGE)
    Local $h_Input = GUICtrlGetHandle($idInput)
    Local $idUpDown = GUICtrlCreateUpdown($idInput)
    Local $hUpDown = GUICtrlGetHandle($idUpDown)

    ; Resize input control
    GUICtrlSetPos($idInput, 150, 100, 100, 40)

    ; Move UpDown control by 2 pixels to prevent clipping
    Local $aPos = ControlGetPos($hGUI, "", "[CLASS:msctls_updown32]")
    GUICtrlSetPos($idUpDown, $aPos[0] + 2, $aPos[1])

    If @OSBuild >= 22000 Then
        ; Apply dark mode to UpDown (spinner) control on Windows 11
        ;_WinAPI_SetWindowTheme($hUpDown, "DarkMode_Explorer") ; pulls resources from DarkMode_Explorer::Spin in theme file
                                                               ; Problem: Windows 10 does not have DarkMode_Explorer::Spin
    EndIf

    If @OSBuild > 10240 And @OSBuild <= 19045 Then
        ; Apply dark mode to UpDown (spinner) control on Windows 10
        ; TODO: This would have to be achieved with subclassing
        ; C++ subclassing examples:
        ;       https://github.com/ozone10/win32-darkmodelib/blob/main/src/DmlibSubclassControl.cpp#L1020
        ;       https://github.com/ozone10/win32-darkmodelib/blob/main/src/Darkmodelib.cpp#L1168
        Local $hDll = DllCallbackRegister(UpDownSub, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
        _WinAPI_SetWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
    EndIf

    Local $hDll = DllCallbackRegister(UpDownSub, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    _WinAPI_SetWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)

    GUISetState(@SW_SHOW)

    Local $idMsg
    ; Loop until the user exits.
    While 1
        $idMsg = GUIGetMsg()

        Switch $idMsg
            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd

    _WinAPI_RemoveWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
    DllCallbackFree($hDll)
EndFunc   ;==>Example

Func _WM_CTLCOLOR($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg
    Local $hDC = $wParam
    Local $hCtrl = $lParam
    _WinAPI_SetTextColor($hDC, 0xFFFFFF)
    Local $hBrush = _WinAPI_CreateSolidBrush(0x202020)
    _WinAPI_SetBkColor($hDC, 0x202020)
    _WinAPI_SetBkMode($hDC, $TRANSPARENT)
    Return $hBrush
EndFunc   ;==>_WM_CTLCOLOR

Func UpDownSub($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Local Static $bHover
    Switch $iMsg
        Case $WM_PAINT
        Local $tPaint, $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)
        Local $tRect = _WinAPI_GetClientRect($hWnd)

        Local $hPen = _WinAPI_CreatePen($PS_SOLID, 1, 0)
        Local $hBrush = _WinAPI_CreateSolidBrush(0x808080)
        _WinAPI_SetBkMode($hDC, $TRANSPARENT)
        _WinAPI_SelectObject($hDC, $hBrush)
        _WinAPI_Rectangle($hDC, $tRect)
        _WinAPI_DrawLine($hDC, 0, Int($tRect.bottom / 2), $tRect.right, Int($tRect.bottom / 2))
        _WinAPI_DeleteObject($hPen)
        _WinAPI_DeleteObject($hBrush)

        If $bHover Then
            Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
            $hBrush = _WinAPI_CreateSolidBrush(0xA0A0A0)
            $tRectTmp = _WinAPI_GetClientRect($hWnd)
            If $iPos Then
            $tRectTmp.top = Int($tRect.bottom / 2)
            Else
            $tRectTmp.bottom = Int($tRect.bottom / 2)
            EndIf
            _WinAPI_SelectObject($hDC, $hBrush)
            _WinAPI_Rectangle($hDC, $tRectTmp)
            _WinAPI_DeleteObject($hBrush)
        EndIf

        _WinAPI_SetTextColor($hDC, 0xFFFFFF)
        Local $hFont = _WinAPI_CreateFont(Int($tRect.bottom / 3), Int($tRect.right / 3))
        _WinAPI_SelectObject($hDC, $hFont)
        $tRect.top += 2
        _WinAPI_DrawText($hDC, "▲", $tRect, $DT_CENTER)
        $tRect.top += Int($tRect.bottom / 2)
        _WinAPI_DrawText($hDC, "▼", $tRect, $DT_CENTER)

        _WinAPI_DeleteObject($hFont)
        _WinAPI_EndPaint($hWnd, $tPaint)
        Case $WM_LBUTTONDOWN
        Local $hBuddy = HWnd(_SendMessage($hWnd, $UDM_GETBUDDY))
        Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
        ControlSetText("", "", $hBuddy, ControlGetText("", "", $hBuddy) + ($iPos ? -1 : +1))
        _SendMessage($hBuddy, $EM_SETSEL, 0, -1)
        Return
        Case $WM_LBUTTONUP
        _WinAPI_InvalidateRect($hWnd, 0, False)
        Return
        Case $WM_MOUSEMOVE
        $bHover = True
        _WinAPI_TrackMouseEvent($hWnd, $TME_LEAVE)
        _WinAPI_InvalidateRect($hWnd, 0, False)
        Return
        Case $WM_MOUSELEAVE
        $bHover = False
        _WinAPI_InvalidateRect($hWnd, 0, False)
        Return
    EndSwitch
    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>UpDownSub

 

Edited by WildByDesign
Posted
7 hours ago, Nine said:

See _WinAPI_CreateCompatibleDC.  You have used that API previously...

Yes, it is included in a few of my projects. But unfortunately, it is copy and pasted and I don't truly understand it.

So I spent a few hours today trying to learn and understand it. I attempted to follow 4 or 5 different examples since they were all slightly different. All of my attempts failed to show anything on the buttons at all.

I really wanted to at least be able to contribute something to this. After failing with the double-buffering stuff, I tried to trigger a button color change on click so that I could at least come back with something. I was able to get the color change working on click. But from all of my attempts, the color would stay until I would move the cursor a bit. I even tried messing with timers and much more. No luck.

You've got something really special started with this. It works very well and looks even better than what Microsoft provides with their own UpDown control. However, my skills and lack of understanding of subclassing is not able to provide anything to build onto what you have started. I apologize. I put in a lot of time and tried my best.

  • Solution
Posted (edited)

I included your control move of the up-down so it looks better when interacting with the input box.

; From Nine
#include <GUIConstants.au3>
#include <WinAPI.au3>
#include <WindowsNotifsConstants.au3>

Global Const $UDM_GETBUDDY = 1130

Example()

Func Example()
  GUICreate("UpDown")

  Local $idInput = GUICtrlCreateInput("0", 10, 10, 60, 25)
  GUICtrlSetFont(-1, 12)
  Local $idUpDown = GUICtrlCreateUpdown($idInput)
  Local $hUpDown = GUICtrlGetHandle(-1)
  GUICtrlSetPos(-1, ControlGetPos("", "", $hUpDown)[0] + 2)

  Local $hDll = DllCallbackRegister(UpDownSub, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
  _WinAPI_SetWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
  GUISetState()

  Do
  Until GUIGetMsg() = $GUI_EVENT_CLOSE

  _WinAPI_RemoveWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
  DllCallbackFree($hDll)
EndFunc   ;==>Example

Func UpDownSub($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
  Local Static $bHover
  Switch $iMsg
    Case $WM_PAINT
      Local $tPaint, $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)
      Local $tRect = _WinAPI_GetClientRect($hWnd)
      Local $hMemDC  = _WinAPI_CreateCompatibleDC($hDC)
      Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $tRect.right, $tRect.bottom)
      Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

      Local $hPen = _WinAPI_CreatePen($PS_SOLID, 1, 0)
      Local $hBrush = _WinAPI_CreateSolidBrush(0x808080)
      _WinAPI_SetBkMode($hMemDC, $TRANSPARENT)
      _WinAPI_SelectObject($hMemDC, $hBrush)
      _WinAPI_Rectangle($hMemDC, $tRect)
      _WinAPI_DrawLine($hMemDC, 0, Int($tRect.bottom / 2), $tRect.right, Int($tRect.bottom / 2))
      _WinAPI_DeleteObject($hPen)
      _WinAPI_DeleteObject($hBrush)

      If $bHover Then
        Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
        $hBrush = _WinAPI_CreateSolidBrush(0xA0A0A0)
        $tRectTmp = _WinAPI_GetClientRect($hWnd)
        If $iPos Then
          $tRectTmp.top = Int($tRect.bottom / 2)
        Else
          $tRectTmp.bottom = Int($tRect.bottom / 2)
        EndIf
        _WinAPI_SelectObject($hMemDC, $hBrush)
        _WinAPI_Rectangle($hMemDC, $tRectTmp)
        _WinAPI_DeleteObject($hBrush)
      EndIf

      _WinAPI_SetTextColor($hMemDC, 0xFFFFFF)
      Local $hFont = _WinAPI_CreateFont(Int($tRect.bottom / 3), Int($tRect.right / 3))
      _WinAPI_SelectObject($hMemDC, $hFont)
      $tRect.top += 2
      _WinAPI_DrawText($hMemDC, "▲", $tRect, $DT_CENTER)
      $tRect.top += Int($tRect.bottom / 2)
      _WinAPI_DrawText($hMemDC, "▼", $tRect, $DT_CENTER)

      _WinAPI_BitBlt($hDC, 0, 0, $tRect.right, $tRect.bottom, $hMemDC, 0, 0, $SRCCOPY)

      _WinAPI_SelectObject($hMemDC, $hOldBmp)
      _WinAPI_DeleteObject($hBitmap)
      _WinAPI_DeleteDC($hMemDC)
      _WinAPI_DeleteObject($hFont)
      _WinAPI_EndPaint($hWnd, $tPaint)
    Case $WM_LBUTTONDOWN
      Local $hBuddy = HWnd(_SendMessage($hWnd, $UDM_GETBUDDY))
      Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
      ControlSetText("", "", $hBuddy, ControlGetText("", "", $hBuddy) + ($iPos ? -1 : +1))
      _SendMessage($hBuddy, $EM_SETSEL, 0, -1)
      Return
    Case $WM_LBUTTONUP
      _WinAPI_InvalidateRect($hWnd, 0, False)
      Return
    Case $WM_MOUSEMOVE
      $bHover = True
      _WinAPI_TrackMouseEvent($hWnd, $TME_LEAVE)
      _WinAPI_InvalidateRect($hWnd, 0, False)
      Return
    Case $WM_MOUSELEAVE
      $bHover = False
      _WinAPI_InvalidateRect($hWnd, 0, False)
      Return
  EndSwitch
  Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>UpDownSub

 

Edited by Nine
Posted
11 minutes ago, Nine said:

I included your control move of the up-down so it looks better when interacting with the input box.

Thank you for the update, I appreciate it. The double-buffering makes a significant difference.

I can immediately see where I went wrong with it. I had everything correct, except that I had my _WinAPI_BitBlt() function in the wrong place.

Posted

I'm finally starting to understand. I've got the "on click" button colour change now. All of the 3-4 methods that I did likely would have worked, but I was missing one thing:

Case $WM_LBUTTONDOWN
; ...
    _WinAPI_InvalidateRect($hWnd, 0, False)
    Return

I was reading up on how to trigger WM_PAINT. Then it was like "Uh Huh!" :)

In the GUIDarkTheme UDF, I will have to check any UpDown controls for UDS_HORZ style and do a horizontal variant of the buttons at some point. But one step at a time.

Question: The two rectangle buttons show a black line around them. Is it possible to change that outline colour? For example, maybe a plain white button outline colour. I am not quite sure what would look best in the end, but I am curious about that.

Anyway, here is my current dark mode interpretation of @Nine's script with the addition of the "on click" button colour change:

DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", @AutoItX64 ? "int64" : "int", -2)

#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>
#include <WindowsStylesConstants.au3>
#include <WinAPITheme.au3>
#include <WinAPISysWin.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPIGdiDC.au3>
#include <APIGdiConstants.au3>
#include <GUIConstants.au3>

#include <WinAPI.au3>
#include <Array.au3>
#include <Misc.au3>

Global Const $UDM_GETBUDDY = 1130
Global $hGUI
Global $hUser32Dll = DllOpen("user32.dll")

Example()

Func Example()
    $hGUI = GUICreate("Dark Mode UpDown", 400, 300)
    GUISetBkColor(0x323232)
    GUISetFont(14)

    ; Apply dark mode to Edit box
    GUIRegisterMsg($WM_CTLCOLOREDIT, "_WM_CTLCOLOR")

    Local $idInput = GUICtrlCreateInput("2", 100, 100, 50, 20, -1, $WS_EX_STATICEDGE)
    Local $h_Input = GUICtrlGetHandle($idInput)
    Local $idUpDown = GUICtrlCreateUpdown($idInput)
    Local $hUpDown = GUICtrlGetHandle($idUpDown)

    ; Resize input control
    GUICtrlSetPos($idInput, 150, 100, 100, 40)

    ; Move UpDown control by 2 pixels to prevent clipping
    GUICtrlSetPos($idUpDown, ControlGetPos("", "", $hUpDown)[0] + 2)

    If @OSBuild >= 22000 Then
        ; Apply dark mode to UpDown (spinner) control on Windows 11
        ;_WinAPI_SetWindowTheme($hUpDown, "DarkMode_Explorer") ; pulls resources from DarkMode_Explorer::Spin in theme file
                                                               ; Problem: Windows 10 does not have DarkMode_Explorer::Spin
    EndIf

    If @OSBuild > 10240 And @OSBuild <= 19045 Then
        ; Apply dark mode to UpDown (spinner) control on Windows 10
        ; TODO: This would have to be achieved with subclassing
        ; C++ subclassing examples:
        ;       https://github.com/ozone10/win32-darkmodelib/blob/main/src/DmlibSubclassControl.cpp#L1020
        ;       https://github.com/ozone10/win32-darkmodelib/blob/main/src/Darkmodelib.cpp#L1168
        Local $hDll = DllCallbackRegister(UpDownSub, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
        _WinAPI_SetWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
    EndIf

    Local $hDll = DllCallbackRegister(UpDownSub, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    _WinAPI_SetWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)

    GUISetState(@SW_SHOW)

    Local $idMsg
    ; Loop until the user exits.
    While 1
        $idMsg = GUIGetMsg()

        Switch $idMsg
            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd

    _WinAPI_RemoveWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
    DllCallbackFree($hDll)
    DllClose($hUser32Dll)
EndFunc   ;==>Example

Func _WM_CTLCOLOR($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg
    Local $hDC = $wParam
    Local $hCtrl = $lParam
    _WinAPI_SetTextColor($hDC, 0xFFFFFF)
    Local $hBrush = _WinAPI_CreateSolidBrush(0x202020)
    _WinAPI_SetBkColor($hDC, 0x202020)
    _WinAPI_SetBkMode($hDC, $TRANSPARENT)
    Return $hBrush
EndFunc   ;==>_WM_CTLCOLOR

Func UpDownSub($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Local Static $bHover
    Switch $iMsg
        Case $WM_PAINT
            Local $tPaint, $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)
            Local $tRect = _WinAPI_GetClientRect($hWnd)
            Local $hMemDC  = _WinAPI_CreateCompatibleDC($hDC)
            Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $tRect.right, $tRect.bottom)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

            Local $hPen = _WinAPI_CreatePen($PS_SOLID, 1, 0)
            Local $hBrush = _WinAPI_CreateSolidBrush(0x242424)
            _WinAPI_SetBkMode($hMemDC, $TRANSPARENT)
            _WinAPI_SelectObject($hMemDC, $hBrush)
            _WinAPI_Rectangle($hMemDC, $tRect)
            _WinAPI_DrawLine($hMemDC, 0, Int($tRect.bottom / 2), $tRect.right, Int($tRect.bottom / 2))
            _WinAPI_DeleteObject($hPen)
            _WinAPI_DeleteObject($hBrush)

            If $bHover Then
                Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
                $hBrush = _WinAPI_CreateSolidBrush(_IsPressed($VK_LBUTTON, $hUser32Dll) ? 0x404040 : 0x808080)
                $tRectTmp = _WinAPI_GetClientRect($hWnd)
                If $iPos Then
                    $tRectTmp.top = Int($tRect.bottom / 2)
                Else
                    $tRectTmp.bottom = Int($tRect.bottom / 2)
                EndIf
                _WinAPI_SelectObject($hMemDC, $hBrush)
                _WinAPI_Rectangle($hMemDC, $tRectTmp)
                _WinAPI_DeleteObject($hBrush)
            EndIf

            _WinAPI_SetTextColor($hMemDC, 0xFFFFFF)
            Local $hFont = _WinAPI_CreateFont(Int($tRect.bottom / 3), Int($tRect.right / 3))
            _WinAPI_SelectObject($hMemDC, $hFont)
            $tRect.top += 2
            _WinAPI_DrawText($hMemDC, "▲", $tRect, $DT_CENTER)
            $tRect.top += Int($tRect.bottom / 2)
            _WinAPI_DrawText($hMemDC, "▼", $tRect, $DT_CENTER)

            _WinAPI_BitBlt($hDC, 0, 0, $tRect.right, $tRect.bottom, $hMemDC, 0, 0, $SRCCOPY)

            _WinAPI_SelectObject($hMemDC, $hOldBmp)
            _WinAPI_DeleteObject($hBitmap)
            _WinAPI_DeleteDC($hMemDC)
            _WinAPI_DeleteObject($hFont)
            _WinAPI_EndPaint($hWnd, $tPaint)
        Case $WM_LBUTTONDOWN
            Local $hBuddy = HWnd(_SendMessage($hWnd, $UDM_GETBUDDY))
            Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
            ControlSetText("", "", $hBuddy, ControlGetText("", "", $hBuddy) + ($iPos ? -1 : +1))
            _SendMessage($hBuddy, $EM_SETSEL, 0, -1)
            _WinAPI_InvalidateRect($hWnd, 0, False)
            Return
        Case $WM_LBUTTONUP
            _WinAPI_InvalidateRect($hWnd, 0, False)
            Return
        Case $WM_MOUSEMOVE
            $bHover = True
            _WinAPI_TrackMouseEvent($hWnd, $TME_LEAVE)
            _WinAPI_InvalidateRect($hWnd, 0, False)
            Return
        Case $WM_MOUSELEAVE
            $bHover = False
            _WinAPI_InvalidateRect($hWnd, 0, False)
            Return
    EndSwitch
    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>UpDownSub

 

Posted

@Nine I have made some really nice progress this morning.

Changes:

  • Added example for UDS_HORZ in addition to the default UpDown control
  • Updated subclass to detect UDS_HORZ and handle UDS_HORZ

I made all of the necessary adjustments to support UDS_HORZ except for the DrawText part.

Could you please help with the DrawText code for UDS_HORZ? :)

This has also made me realize that we might need to know what type of class the buddy control is to help determine what should be done when clicking on the up/down buttons. I'm not even sure what we should do when it comes to tab controls and other control types. For now, I have added the following for whenever a button is clicked:

Case $WM_LBUTTONDOWN
    Local $hBuddy = HWnd(_SendMessage($hWnd, $UDM_GETBUDDY))
    ; ...
    Local $iBuddyClass = _WinAPI_GetClassName($hBuddy)
    ConsoleWrite("What control class type is buddy? " & $iBuddyClass & @CRLF)

And here is the updated script that has detection and handling of UDS_HORZ:

DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", @AutoItX64 ? "int64" : "int", -2)

#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>
#include <WindowsStylesConstants.au3>
#include <WinAPITheme.au3>
#include <WinAPISysWin.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPIGdiDC.au3>
#include <APIGdiConstants.au3>
#include <GUIConstants.au3>

#include <WinAPI.au3>
#include <Array.au3>
#include <Misc.au3>

Global Const $UDM_GETBUDDY = 1130
Global $hGUI
Global $hUser32Dll = DllOpen("user32.dll")

Example()

Func Example()
    $hGUI = GUICreate("Dark Mode UpDown", 400, 300)
    GUISetBkColor(0x323232)
    GUISetFont(14)

    ; Apply dark mode to Edit box
    GUIRegisterMsg($WM_CTLCOLOREDIT, "_WM_CTLCOLOR")

    Local $idInput = GUICtrlCreateInput("2", 150, 80, 100, 40, -1, $WS_EX_STATICEDGE)
    Local $h_Input = GUICtrlGetHandle($idInput)
    Local $idUpDown = GUICtrlCreateUpdown($idInput)
    Local $hUpDown = GUICtrlGetHandle($idUpDown)

    Local $idInput2 = GUICtrlCreateInput("2", 150, 140, 100, 40, -1, $WS_EX_STATICEDGE)
    Local $h_Input2 = GUICtrlGetHandle($idInput2)
    Local $idUpDown2 = GUICtrlCreateUpdown($idInput2, $UDS_HORZ) ; UDS_HORZ testing
    Local $hUpDown2 = GUICtrlGetHandle($idUpDown2)

    ; Move UpDown control by 2 pixels to prevent clipping
    GUICtrlSetPos($idUpDown, ControlGetPos("", "", $hUpDown)[0] + 2)
    GUICtrlSetPos($idUpDown2, ControlGetPos("", "", $hUpDown2)[0] + 2)

    Local $hDll = DllCallbackRegister(UpDownSub, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    _WinAPI_SetWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
    _WinAPI_SetWindowSubclass($hUpDown2, DllCallbackGetPtr($hDll), $idUpDown2)

    GUISetState(@SW_SHOW)

    Local $idMsg
    ; Loop until the user exits.
    While 1
        $idMsg = GUIGetMsg()

        Switch $idMsg
            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd

    _WinAPI_RemoveWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
    _WinAPI_RemoveWindowSubclass($hUpDown2, DllCallbackGetPtr($hDll), $idUpDown2)
    DllCallbackFree($hDll)
    DllClose($hUser32Dll)
EndFunc   ;==>Example

Func _WM_CTLCOLOR($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg
    Local $hDC = $wParam
    Local $hCtrl = $lParam
    _WinAPI_SetTextColor($hDC, 0xFFFFFF)
    Local $hBrush = _WinAPI_CreateSolidBrush(0x202020)
    _WinAPI_SetBkColor($hDC, 0x202020)
    _WinAPI_SetBkMode($hDC, $TRANSPARENT)
    Return $hBrush
EndFunc   ;==>_WM_CTLCOLOR

Func UpDownSub($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Local Static $bHover
    Local $bHorz = BitAND(_WinAPI_GetWindowLong($hWnd, $GWL_STYLE), $UDS_HORZ)
    Switch $iMsg
        Case $WM_PAINT
            Local $tPaint, $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)
            Local $tRect = _WinAPI_GetClientRect($hWnd)
            Local $hMemDC  = _WinAPI_CreateCompatibleDC($hDC)
            Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $tRect.right, $tRect.bottom)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

            Local $hPen = _WinAPI_CreatePen($PS_SOLID, 1, 0)
            Local $hBrush = _WinAPI_CreateSolidBrush(0x242424)
            _WinAPI_SetBkMode($hMemDC, $TRANSPARENT)
            _WinAPI_SelectObject($hMemDC, $hBrush)
            _WinAPI_Rectangle($hMemDC, $tRect)
            If $bHorz Then
                _WinAPI_DrawLine($hMemDC, Int($tRect.right / 2), 0, Int($tRect.right / 2), $tRect.bottom)
            Else
                _WinAPI_DrawLine($hMemDC, 0, Int($tRect.bottom / 2), $tRect.right, Int($tRect.bottom / 2))
            EndIf
            _WinAPI_DeleteObject($hPen)
            _WinAPI_DeleteObject($hBrush)

            If $bHover Then
                If $bHorz Then
                    Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).x / _WinAPI_GetClientRect($hWnd).right, 0)
                Else
                    Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
                EndIf
                $hBrush = _WinAPI_CreateSolidBrush(_IsPressed($VK_LBUTTON, $hUser32Dll) ? 0x404040 : 0x808080)
                $tRectTmp = _WinAPI_GetClientRect($hWnd)
                If $iPos Then
                    If $bHorz Then
                        $tRectTmp.left = Int($tRect.right / 2)
                    Else
                        $tRectTmp.top = Int($tRect.bottom / 2)
                    EndIf
                Else
                    If $bHorz Then
                        $tRectTmp.right = Int($tRect.right / 2)
                    Else
                        $tRectTmp.bottom = Int($tRect.bottom / 2)
                    EndIf
                EndIf
                _WinAPI_SelectObject($hMemDC, $hBrush)
                _WinAPI_Rectangle($hMemDC, $tRectTmp)
                _WinAPI_DeleteObject($hBrush)
            EndIf

            _WinAPI_SetTextColor($hMemDC, 0xFFFFFF)
            Local $hFont = _WinAPI_CreateFont(Int($tRect.bottom / 3), Int($tRect.right / 3))
            _WinAPI_SelectObject($hMemDC, $hFont)
            $tRect.top += 2
            If Not $bHorz Then _WinAPI_DrawText($hMemDC, "▲", $tRect, $DT_CENTER)
            $tRect.top += Int($tRect.bottom / 2)
            If Not $bHorz Then _WinAPI_DrawText($hMemDC, "▼", $tRect, $DT_CENTER)

            _WinAPI_BitBlt($hDC, 0, 0, $tRect.right, $tRect.bottom, $hMemDC, 0, 0, $SRCCOPY)

            _WinAPI_SelectObject($hMemDC, $hOldBmp)
            _WinAPI_DeleteObject($hBitmap)
            _WinAPI_DeleteDC($hMemDC)
            _WinAPI_DeleteObject($hFont)
            _WinAPI_EndPaint($hWnd, $tPaint)
        Case $WM_LBUTTONDOWN
            Local $hBuddy = HWnd(_SendMessage($hWnd, $UDM_GETBUDDY))
            Local $iBuddyClass = _WinAPI_GetClassName($hBuddy)
            ConsoleWrite("What control class type is buddy? " & $iBuddyClass & @CRLF)
            If $bHorz Then
                Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).x / _WinAPI_GetClientRect($hWnd).right, 0)
            Else
                Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
            EndIf
            ControlSetText("", "", $hBuddy, ControlGetText("", "", $hBuddy) + ($iPos ? -1 : +1))
            _SendMessage($hBuddy, $EM_SETSEL, 0, -1)
            _WinAPI_InvalidateRect($hWnd, 0, False)
            Return
        Case $WM_LBUTTONUP
            _WinAPI_InvalidateRect($hWnd, 0, False)
            Return
        Case $WM_MOUSEMOVE
            $bHover = True
            _WinAPI_TrackMouseEvent($hWnd, $TME_LEAVE)
            _WinAPI_InvalidateRect($hWnd, 0, False)
            Return
        Case $WM_MOUSELEAVE
            $bHover = False
            _WinAPI_InvalidateRect($hWnd, 0, False)
            Return
    EndSwitch
    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>UpDownSub

 

Posted
3 hours ago, WildByDesign said:

Could you please help with the DrawText code for UDS_HORZ?

_WinAPI_SetTextColor($hMemDC, 0xFFFFFF)
            $tRectTmp = _WinAPI_GetClientRect($hWnd)
            Local $iFW = Int($tRect.right / ($bHorz ? 5 : 3)), $iFH = $iFW + ($bHorz ? 14 : 2)
            Local $hFont = _WinAPI_CreateFont($iFH, $iFW)
            _WinAPI_SelectObject($hMemDC, $hFont)
            If $bHorz Then
              $tRectTmp.top = Int(($tRect.bottom - $iFH) / 2)
              $tRectTmp.right = $tRect.right / 2
              _WinAPI_DrawText($hMemDC, "◄", $tRectTmp, $DT_CENTER)
              $tRectTmp.left = Int($tRect.right/2)
              $tRectTmp.right = $tRect.right
              _WinAPI_DrawText($hMemDC, "►", $tRectTmp, $DT_CENTER)
            Else
              $tRectTmp.top = Int((Round($tRect.bottom / 2) - $iFH) / 2)
              _WinAPI_DrawText($hMemDC, "▲", $tRectTmp, $DT_CENTER)
              $tRectTmp.top += Round($tRect.bottom / 2)
              _WinAPI_DrawText($hMemDC, "▼", $tRectTmp, $DT_CENTER)
            EndIf

Also you would want to change to XOR :

Case $WM_LBUTTONDOWN
            Local $hBuddy = HWnd(_SendMessage($hWnd, $UDM_GETBUDDY))
            Local $iBuddyClass = _WinAPI_GetClassName($hBuddy)
            ConsoleWrite("What control class type is buddy? " & $iBuddyClass & @CRLF)
            If $bHorz Then
                Local $iPos = BitXOR(Round(_WinAPI_GetMousePos(True, $hWnd).x / _WinAPI_GetClientRect($hWnd).right, 0), 1) ; <<<<<<<<<<< this line
            Else
                Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
            EndIf
            ControlSetText("", "", $hBuddy, ControlGetText("", "", $hBuddy) + ($iPos ? -1 : +1))
            _SendMessage($hBuddy, $EM_SETSEL, 0, -1)
            _WinAPI_InvalidateRect($hWnd, 0, False)
            Return

 

Posted

Thank you. The DrawText code works perfectly and looks very nice too. I appreciate your help with this. 

4 hours ago, Nine said:

Also you would want to change to XOR :

Your updated line with BitXOR works great. I must admit, I am not very familiar with BitXOR.

I have another line that is similar:

If $bHover Then
    If $bHorz Then
        Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).x / _WinAPI_GetClientRect($hWnd).right, 0)
    Else
        Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
    EndIf

Does that similar line under "If $bHorz Then" also need the BitXOR?

Posted
On 4/10/2026 at 9:57 AM, WildByDesign said:

Moved the UpDown control to the right by 2 pixels to prevent clipping by Edit control (Windows issue with UpDown controls by default)

It looks like I will also have to check for UDS_ALIGNLEFT style and in that case, minus 2 pixels to move the UpDown control.

Posted (edited)

I have made some more progress. It is very close to being ready to add to GUIDarkTheme UDF. :)

  • Added tab control to ensure that we can cycle through the tabs
  • Realized that _SendMessage($hWnd, $UDM_GETBUDDY) does not seem to work to get tab control handle
  • Realized that we can just get rid of the cases for $WM_LBUTTONDOWN and $WM_LBUTTONUP
    • The OS seems to handle this exactly as needed for Edit boxes and tab controls

First of all, sorry for the blinding contrast with the tab control. I wanted to keep the example small and therefore did not add the dark mode subclassing for the tab control.

The only thing holding me back right now from adding it to the UDF is the arrow sizes. You'll notice that the arrow sizes are quite large for the tab control's UpDown. So I decided to look into the theme file (aero.msstyles) to see how Microsoft does it. Also by temporarily disabling the UpDown subclassing to check arrow sizes. So it seems that Microsoft uses the same size arrows, no matter the size of the UpDown buttons. The same size arrows differing by DPI, of course.

I am just trying to figure out how to consolidate the arrow sizes to be consistent but when I mess with _WinAPI_CreateFont sizes it also messes with the placement/alignment within the buttons as well, so this part is complicated for me.

20 hours ago, Nine said:

Just set the pen color you want, and select the object for the hMemDC (as per MSDN)

When I change your:

Local $hPen = _WinAPI_CreatePen($PS_SOLID, 1, 0)

To:

Local $hPen = _WinAPI_CreatePen($PS_SOLID, 1, 0xFFFFFF)

It still comes up as black lines. This is an area that I am not very familiar with.

Anyway, here is the my current testing script with the addition of tab control (also one of the Edit boxes now has $UDS_ALIGNLEFT for testing UpDown):

DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", @AutoItX64 ? "int64" : "int", -2)

#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>
#include <WindowsStylesConstants.au3>
#include <WinAPITheme.au3>
#include <WinAPISysWin.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPIGdiDC.au3>
#include <APIGdiConstants.au3>
#include <GUIConstants.au3>
#include <WinAPI.au3>
#include <Misc.au3>

Global Const $UDM_GETBUDDY = 1130
Global $hGUI
Global $hUser32Dll = DllOpen("user32.dll")

Example()

Func Example()
    $hGUI = GUICreate("Dark Mode UpDown", 400, 400)
    GUISetBkColor(0x323232)
    GUISetFont(14)

    ; Apply dark mode to Edit box
    GUIRegisterMsg($WM_CTLCOLOREDIT, "_WM_CTLCOLOR")

    Local $idInput = GUICtrlCreateInput("2", 150, 80, 100, 40, -1, $WS_EX_STATICEDGE)
    Local $h_Input = GUICtrlGetHandle($idInput)
    Local $idUpDown = GUICtrlCreateUpdown($idInput, $UDS_ALIGNLEFT)
    Local $hUpDown = GUICtrlGetHandle($idUpDown)

    Local $idInput2 = GUICtrlCreateInput("2", 150, 140, 100, 40, -1, $WS_EX_STATICEDGE)
    Local $h_Input2 = GUICtrlGetHandle($idInput2)
    Local $idUpDown2 = GUICtrlCreateUpdown($idInput2, $UDS_HORZ) ; UDS_HORZ testing
    Local $hUpDown2 = GUICtrlGetHandle($idUpDown2)

    Local $iBuddyPos

    ; Move UpDown control by 2 pixels to prevent clipping
    If BitAND(_WinAPI_GetWindowLong($hUpDown, $GWL_STYLE), $UDS_ALIGNLEFT) Then
        $iBuddyPos = -2
    Else
        $iBuddyPos = 2
    EndIf
    GUICtrlSetPos($idUpDown, ControlGetPos("", "", $hUpDown)[0] + $iBuddyPos)

    If BitAND(_WinAPI_GetWindowLong($hUpDown2, $GWL_STYLE), $UDS_ALIGNLEFT) Then
        $iBuddyPos = -2
    Else
        $iBuddyPos = 2
    EndIf
    GUICtrlSetPos($idUpDown2, ControlGetPos("", "", $hUpDown2)[0] + $iBuddyPos)

    Local $hDll = DllCallbackRegister(UpDownSub, "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    _WinAPI_SetWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
    _WinAPI_SetWindowSubclass($hUpDown2, DllCallbackGetPtr($hDll), $idUpDown2)

    ; tab control test
    GUISetFont(9)
    Local $idTab = GUICtrlCreateTab(100, 200, 200, 100)
    Local $h_Tab = GUICtrlGetHandle($idTab)
    GUICtrlCreateTabItem("tab0")
    GUICtrlCreateTabItem("tab1")
    GUICtrlCreateTabItem("tab2")
    GUICtrlCreateTabItem("tab3")
    GUICtrlCreateTabItem("tab4")
    GUICtrlCreateTabItem("")

    ; find updown control
    Local $hUpDown3 = _WinAPI_FindWindowEx($h_Tab, 0, "msctls_updown32", "")
    _WinAPI_SetWindowSubclass($hUpDown3, DllCallbackGetPtr($hDll), $idTab)

    GUISetState(@SW_SHOW)

    Local $idMsg
    ; Loop until the user exits.
    While 1
        $idMsg = GUIGetMsg()

        Switch $idMsg
            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd

    _WinAPI_RemoveWindowSubclass($hUpDown, DllCallbackGetPtr($hDll), $idUpDown)
    _WinAPI_RemoveWindowSubclass($hUpDown2, DllCallbackGetPtr($hDll), $idUpDown2)
    _WinAPI_RemoveWindowSubclass($hUpDown3, DllCallbackGetPtr($hDll), $idTab)
    DllCallbackFree($hDll)
    DllClose($hUser32Dll)
EndFunc   ;==>Example

Func _WM_CTLCOLOR($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg
    Local $hDC = $wParam
    Local $hCtrl = $lParam
    _WinAPI_SetTextColor($hDC, 0xFFFFFF)
    Local $hBrush = _WinAPI_CreateSolidBrush(0x202020)
    _WinAPI_SetBkColor($hDC, 0x202020)
    _WinAPI_SetBkMode($hDC, $TRANSPARENT)
    Return $hBrush
EndFunc   ;==>_WM_CTLCOLOR

Func UpDownSub($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
    Local Static $bHover
    Local $bHorz = BitAND(_WinAPI_GetWindowLong($hWnd, $GWL_STYLE), $UDS_HORZ)
    Switch $iMsg
        Case $WM_PAINT
            Local $tPaint, $hDC = _WinAPI_BeginPaint($hWnd, $tPaint)
            Local $tRect = _WinAPI_GetClientRect($hWnd)
            Local $hMemDC  = _WinAPI_CreateCompatibleDC($hDC)
            Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $tRect.right, $tRect.bottom)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hBitmap)

            Local $hPen = _WinAPI_CreatePen($PS_SOLID, 1, 0)
            Local $hBrush = _WinAPI_CreateSolidBrush(0x242424)
            _WinAPI_SetBkMode($hMemDC, $TRANSPARENT)
            _WinAPI_SelectObject($hMemDC, $hBrush)
            _WinAPI_Rectangle($hMemDC, $tRect)
            If $bHorz Then
                _WinAPI_DrawLine($hMemDC, Int($tRect.right / 2), 0, Int($tRect.right / 2), $tRect.bottom)
            Else
                _WinAPI_DrawLine($hMemDC, 0, Int($tRect.bottom / 2), $tRect.right, Int($tRect.bottom / 2))
            EndIf
            _WinAPI_DeleteObject($hPen)
            _WinAPI_DeleteObject($hBrush)

            If $bHover Then
                If $bHorz Then
                    Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).x / _WinAPI_GetClientRect($hWnd).right, 0)
                Else
                    Local $iPos = Round(_WinAPI_GetMousePos(True, $hWnd).y / _WinAPI_GetClientRect($hWnd).bottom, 0)
                EndIf
                $hBrush = _WinAPI_CreateSolidBrush(_IsPressed($VK_LBUTTON, $hUser32Dll) ? 0x404040 : 0x606060)
                $tRectTmp = _WinAPI_GetClientRect($hWnd)
                If $iPos Then
                    If $bHorz Then
                        $tRectTmp.left = Int($tRect.right / 2)
                    Else
                        $tRectTmp.top = Int($tRect.bottom / 2)
                    EndIf
                Else
                    If $bHorz Then
                        $tRectTmp.right = Int($tRect.right / 2)
                    Else
                        $tRectTmp.bottom = Int($tRect.bottom / 2)
                    EndIf
                EndIf
                _WinAPI_SelectObject($hMemDC, $hBrush)
                _WinAPI_Rectangle($hMemDC, $tRectTmp)
                _WinAPI_DeleteObject($hBrush)
            EndIf

            _WinAPI_SetTextColor($hMemDC, 0xFFFFFF)
            $tRectTmp = _WinAPI_GetClientRect($hWnd)
            Local $iFW = Int($tRect.right / ($bHorz ? 5 : 3)), $iFH = $iFW + ($bHorz ? 14 : 2)
            Local $hFont = _WinAPI_CreateFont($iFH, $iFW)
            _WinAPI_SelectObject($hMemDC, $hFont)
            If $bHorz Then
                $tRectTmp.top = Int(($tRect.bottom - $iFH) / 2)
                $tRectTmp.right = $tRect.right / 2
                _WinAPI_DrawText($hMemDC, "◄", $tRectTmp, BitOR($DT_CENTER, $DT_VCENTER, $DT_NOCLIP))
                $tRectTmp.left = Int($tRect.right / 2)
                $tRectTmp.right = $tRect.right
                _WinAPI_DrawText($hMemDC, "►", $tRectTmp, BitOR($DT_CENTER, $DT_VCENTER, $DT_NOCLIP))
            Else
                $tRectTmp.top = Int((Round($tRect.bottom / 2) - $iFH) / 2)
                _WinAPI_DrawText($hMemDC, "▲", $tRectTmp, BitOR($DT_CENTER, $DT_VCENTER, $DT_NOCLIP))
                $tRectTmp.top += Round($tRect.bottom / 2)
                _WinAPI_DrawText($hMemDC, "▼", $tRectTmp, BitOR($DT_CENTER, $DT_VCENTER, $DT_NOCLIP))
            EndIf

            _WinAPI_BitBlt($hDC, 0, 0, $tRect.right, $tRect.bottom, $hMemDC, 0, 0, $SRCCOPY)

            _WinAPI_SelectObject($hMemDC, $hOldBmp)
            _WinAPI_DeleteObject($hBitmap)
            _WinAPI_DeleteDC($hMemDC)
            _WinAPI_DeleteObject($hFont)
            _WinAPI_EndPaint($hWnd, $tPaint)
        Case $WM_MOUSEMOVE
            $bHover = True
            _WinAPI_TrackMouseEvent($hWnd, $TME_LEAVE)
            _WinAPI_InvalidateRect($hWnd, 0, False)
            Return
        Case $WM_MOUSELEAVE
            $bHover = False
            _WinAPI_InvalidateRect($hWnd, 0, False)
            Return
    EndSwitch
    Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>UpDownSub

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

 

Edited by WildByDesign
Posted
43 minutes ago, WildByDesign said:

It still comes up as black lines

You are missing the select part of my comment.  Add the following line after pen creation :

_WinAPI_SelectObject($hMemDC, $hPen)

Also I see better result in my case when the up-down control is moved only 1 pixel instead of 2 (right and left).

ps. Your click color change resolves the issue with the buddy.  Good catch.

Posted
3 minutes ago, Nine said:

You are missing the select part of my comment.  Add the following line after pen creation :

You're right. Adding that fixed my issue with changing pen colour. I've got a lot to learn with the WinAPI stuff. Thank you. :)

4 minutes ago, Nine said:

Also I see better result in my case when the up-down control is moved only 1 pixel instead of 2 (right and left).

Interesting, I appreciate this feedback. This makes me wonder if the number of pixels needed to move depends on DPI. My scaling is 125% and 2 pixels works best for mine. I would then assume that users with even higher scaling may need slightly differing pixel values. I will have to dig into this some more.

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