Jump to content

Recommended Posts

Posted

I have a need for my GUI to be black (black background, light text) for applying Windows 11 backdrop materials. I use a script from @pixelsearch that allows customizing the colors of the statusbar and everything works beautifully.

Until recently, I also had a need for using _GUICtrlStatusBar_EmbedControl to embed a combobox on the statusbar. I also realized that adding text with labels to the statusbar with _GUICtrlStatusBar_EmbedControl gave better control over fonts, DPI scaling, etc.

Anyway, statusbar Part 0 is as expected with black background. Part 1 is just an example with a smaller sized label showing how the embed changes the coloring (font and background). Parts 2 and 3 are label and stretched.

I have tried making the labels transparent or setting specific colors, but the _GUICtrlStatusBar_EmbedControl automatically reverts those colors. If anybody has any ideas or solutions for this problem, please let me know. Thank you so much.

Script:

Spoiler
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <GuiStatusBar.au3>
#include <StaticConstants.au3>
#include <WinAPIGdi.au3>
#include <WinAPIRes.au3>
#include <WinAPISysWin.au3>
#include <WinAPITheme.au3>
#include <WindowsConstants.au3>

#include <ComboConstants.au3>
#include <EditConstants.au3>
#include <GuiComboBox.au3>
#include <FontConstants.au3>

Opt("MustDeclareVars", 1)

Global $hGUI, $g_hSizebox, $g_hOldProc, $g_hStatus, $g_iHeight, $g_aText, $g_aRatioW, $g_iBkColor, $g_iTextColor, $g_hDots
; Global $g_hBrush ; no need, as _GUICtrlStatusBar_SetBkColor() placed after _WinAPI_SetWindowTheme() does the job correctly

Global $idInput, $idComboBox

Example()

;==============================================
Func Example()

    _GDIPlus_Startup()

    Local Const $SBS_SIZEBOX = 0x08, $SBS_SIZEGRIP = 0x10
    Local $iW = 800, $iH = 600
    Dim $g_iBkColor = 0x00000000, $g_iTextColor = 0xFFFFFF

    $hGUI = GUICreate("Resize corner", $iW, $iH, -1, -1)
    GUISetBkColor($g_iBkColor)
    GUICtrlSetDefColor(0xffffff)
    GUISetFont(9,  $FW_NORMAL, $GUI_FONTNORMAL, "Segoe UI")

    ;-----------------
    ; Create a sizebox window (Scrollbar class) BEFORE creating the StatusBar control
    $g_hSizebox = _WinAPI_CreateWindowEx(0, "Scrollbar", "", $WS_CHILD + $WS_VISIBLE + $SBS_SIZEBOX, _
        0, 0, 0, 0, $hGUI) ; $SBS_SIZEBOX or $SBS_SIZEGRIP

    ; Subclass the sizebox (by changing the window procedure associated with the Scrollbar class)
    Local $hProc = DllCallbackRegister('ScrollbarProc', 'lresult', 'hwnd;uint;wparam;lparam')
    $g_hOldProc = _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, DllCallbackGetPtr($hProc))

    Local $hCursor = _WinAPI_LoadCursor(0, $OCR_SIZENWSE)
    _WinAPI_SetClassLongEx($g_hSizebox, -12, $hCursor) ; $GCL_HCURSOR = -12

    ; $g_hBrush = _WinAPI_CreateSolidBrush($g_iBkColor)

    ;-----------------
    $g_hStatus = _GUICtrlStatusBar_Create($hGUI, -1, "", $WS_CLIPSIBLINGS) ; ClipSiblings style +++
    Local $aParts[4] = [200, 400, 600, -1]
    If $aParts[Ubound($aParts) - 1] = -1 Then $aParts[Ubound($aParts) - 1] = $iW ; client width size
    _MyGUICtrlStatusBar_SetParts($g_hStatus, $aParts)

    Dim $g_aText[Ubound($aParts)] = ["Part 0", "", "", ""]
    Dim $g_aRatioW[Ubound($aParts)]
    For $i = 0 To UBound($g_aText) - 1
        _GUICtrlStatusBar_SetText($g_hStatus, "", $i, $SBT_OWNERDRAW)
        ; _GUICtrlStatusBar_SetText($g_hStatus, "", $i, $SBT_OWNERDRAW + $SBT_NOBORDERS) ; interesting ?
        $g_aRatioW[$i] = $aParts[$i] / $iW
    Next


    ; to allow the setting of StatusBar BkColor at least under Windows 10
    _WinAPI_SetWindowTheme($g_hStatus, "", "")

    ; Set status bar background color
    _GUICtrlStatusBar_SetBkColor($g_hStatus, $g_iBkColor)

    $g_iHeight = _GUICtrlStatusBar_GetHeight($g_hStatus) + 3 ; change the constant (+3) if necessary
    $g_hDots = CreateDots($g_iHeight, $g_iHeight, 0xFF000000 + $g_iBkColor, 0xFF000000 + $g_iTextColor)

    ;Local $idInput = GUICtrlCreateInput("embed", 0, 0)
    ;Local $hInput = GUICtrlGetHandle($idInput)
    $idComboBox = GUICtrlCreateCombo("Install", 10, 10, 185, 20, $CBS_DROPDOWNLIST)
    GUICtrlSetState(-1, $GUI_HIDE)
    ;Local $hidComboBox = GUICtrlGetHandle($idComboBox)
    ;GUICtrlSetData($idComboBox, "Uninstall")
    $idInput = GUICtrlCreateInput("Startup Task  ▼", 20, 40, 340, 20, $ES_READONLY, 0)
    ;Local $idPart0 = GUICtrlCreateLabel("Part 0", 0, 0, -1, -1)
    GUICtrlSetColor(-1, 0xffffff)
    Local $idPart1 = GUICtrlCreateLabel("Part 1", 0, 0, -1, -1)
    Local $idPart2 = GUICtrlCreateLabel("Part 2", 0, 0, -1, -1)
    ;Local $hInput = GUICtrlGetHandle($idInput)
    _GUICtrlStatusBar_EmbedControl($g_hStatus, 3, GUICtrlGetHandle($idComboBox), 4)
    _GUICtrlStatusBar_EmbedControl($g_hStatus, 3, GUICtrlGetHandle($idInput), 4)
    _GUICtrlStatusBar_EmbedControl($g_hStatus, 2, GUICtrlGetHandle($idPart2), 4)
    _GUICtrlStatusBar_EmbedControl($g_hStatus, 1, GUICtrlGetHandle($idPart1), 1 + 2)
    ;_GUICtrlStatusBar_EmbedControl($g_hStatus, 0, GUICtrlGetHandle($idPart0), 1)

    GUIRegisterMsg($WM_SIZE, "WM_SIZE")
    GUIRegisterMsg($WM_MOVE, "WM_MOVE")
    GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM")
    GUIRegisterMsg($WM_COMMAND, "ED_WM_COMMAND")

    GUICtrlSendMsg( $idComboBox, $WM_CHANGEUISTATE, 65537, 0 )

    GUISetState()

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $idComboBox
                ;GUICtrlSetData($idInput, GUICtrlRead($idComboBox))
                Local $comboread = GUICtrlRead($idComboBox)
                If $comboread = "Install" Then
                    ConsoleWrite("you choose to install task" & @CRLF)
                    GUICtrlSetData($idComboBox, "")
                    GUICtrlSetData($idComboBox, "Uninstall")
                    GUICtrlSetData($idPart2, "installed")
                EndIf
                If $comboread = "Uninstall" Then
                    ConsoleWrite("you choose to uninstall task" & @CRLF)
                    GUICtrlSetData($idComboBox, "")
                    GUICtrlSetData($idComboBox, "Install")
                    GUICtrlSetData($idPart2, "not installed")
                EndIf
        EndSwitch
    WEnd

    _GDIPlus_BitmapDispose($g_hDots)
    _GUICtrlStatusBar_Destroy($g_hStatus)
    _WinAPI_DestroyCursor($hCursor)
    ; _WinAPI_DeleteObject($g_hBrush)
    _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, $g_hOldProc)
    DllCallbackFree($hProc)
    _GDIPlus_Shutdown()
EndFunc   ;==>Example

;==============================================
Func ScrollbarProc($hWnd, $iMsg, $wParam, $lParam) ; Andreik

    If $iMsg = $WM_PAINT Then
        Local $tPAINTSTRUCT
        Local $hDC = _WinAPI_BeginPaint($hWnd, $tPAINTSTRUCT)
        Local $iWidth = DllStructGetData($tPAINTSTRUCT, 'rPaint', 3) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 1)
        Local $iHeight = DllStructGetData($tPAINTSTRUCT, 'rPaint', 4) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 2)
        Local $hGraphics = _GDIPlus_GraphicsCreateFromHDC($hDC)
        _GDIPlus_GraphicsDrawImageRect($hGraphics, $g_hDots, 0, 0, $iWidth, $iHeight)
        _GDIPlus_GraphicsDispose($hGraphics)
        _WinAPI_EndPaint($hWnd, $tPAINTSTRUCT)
        Return 0
    EndIf
    Return _WinAPI_CallWindowProc($g_hOldProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>ScrollbarProc

;==============================================
Func CreateDots($iWidth, $iHeight, $iBackgroundColor, $iDotsColor) ; Andreik

    Local $iDotSize = Int($iHeight / 10)
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    Local $hBrush = _GDIPlus_BrushCreateSolid($iDotsColor)
    _GDIPlus_GraphicsClear($hGraphics, $iBackgroundColor)
    Local $a[6][2] = [[2,6], [2,4], [2,2], [4,4], [4,2], [6,2]]
    For $i = 0 To UBound($a) - 1
        _GDIPlus_GraphicsFillRect($hGraphics, $iWidth - $iDotSize * $a[$i][0], $iHeight - $iDotSize * $a[$i][1], $iDotSize, $iDotSize, $hBrush)
    Next
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hGraphics)
    Return $hBitmap
EndFunc   ;==>CreateDots

;==============================================
Func _MyGUICtrlStatusBar_SetParts($hWnd, $aPartEdge) ; Pixelsearch

    If Not IsArray($aPartEdge) Then Return False
    Local $iParts = UBound($aPartEdge)
    Local $tParts = DllStructCreate("int[" & $iParts & "]")
    For $i = 0 To $iParts - 1
        DllStructSetData($tParts, 1, $aPartEdge[$i], $i + 1)
    Next
    DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $hWnd, "uint", $SB_SETPARTS, "wparam", $iParts, "struct*", $tParts)
    _GUICtrlStatusBar_Resize($hWnd)
    Return True
EndFunc   ;==>_MyGUICtrlStatusBar_SetParts

;==============================================
Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam) ; Pixelsearch
    #forceref $iMsg, $wParam, $lParam

    If $hWnd = $hGUI Then
        Local Static $bIsSizeBoxShown = True
        Local $aSize = WinGetClientSize($hGUI)
        Local $aGetParts = _GUICtrlStatusBar_GetParts($g_hStatus)
        Local $aParts[$aGetParts[0]]
        For $i = 0 To $aGetParts[0] - 1
            $aParts[$i] = Int($aSize[0] * $g_aRatioW[$i])
        Next
        If BitAND(WinGetState($hGUI), $WIN_STATE_MAXIMIZED) Then
            _GUICtrlStatusBar_SetParts($g_hStatus, $aParts) ; set parts until GUI right border (AutoIt function forces it)
            _WinAPI_ShowWindow($g_hSizebox, @SW_HIDE)
            $bIsSizeBoxShown = False
        Else
            If $g_aRatioW[UBound($aParts) -1] <> 1 Then $aParts[UBound($aParts) -1] = $aSize[0] - $g_iHeight ; right size of right part stuck on sizebox (no gap)
            _MyGUICtrlStatusBar_SetParts($g_hStatus, $aParts) ; set parts until sizebox (see preceding line) or until GUI right border
            WinMove($g_hSizebox, "", $aSize[0] - $g_iHeight, $aSize[1] - $g_iHeight, $g_iHeight, $g_iHeight)
            If Not $bIsSizeBoxShown Then
                _WinAPI_ShowWindow($g_hSizebox, @SW_SHOW)
                $bIsSizeBoxShown = True
            EndIf
        EndIf
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_SIZE

;==============================================
Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam

    If $hWnd = $hGUI Then
        _WinAPI_RedrawWindow($g_hSizebox)
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE

;==============================================
Func WM_DRAWITEM($hWnd, $iMsg, $wParam, $lParam) ; Kafu
    #forceref $hWnd, $iMsg, $wParam

    Local Static $tagDRAWITEM = "uint CtlType;uint CtlID;uint itemID;uint itemAction;uint itemState;hwnd hwndItem;handle hDC;long rcItem[4];ulong_ptr itemData"
    Local $tDRAWITEM = DllStructCreate($tagDRAWITEM, $lParam)
    If $tDRAWITEM.hwndItem <> $g_hStatus Then Return $GUI_RUNDEFMSG ; only process the statusbar

    Local $itemID = $tDRAWITEM.itemID ; status bar part number : 0 if simple bar (or 0, 1, 2... if multi parts)
    Local $hDC = $tDRAWITEM.hDC
    Local $tRect = DllStructCreate("long left;long top;long right;long bottom", DllStructGetPtr($tDRAWITEM, "rcItem"))
    ; _WinAPI_FillRect($hDC, DllStructGetPtr($tRect), $g_hBrush) ; backgound color
    _WinAPI_SetTextColor($hDC, $g_iTextColor) ; text color
    _WinAPI_SetBkMode($hDC, $TRANSPARENT)
    DllStructSetData($tRect, "top", $tRect.top + 1)
    DllStructSetData($tRect, "left", $tRect.left + 1)
    _WinAPI_DrawText($hDC, $g_aText[$itemID], $tRect, $DT_LEFT)

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_DRAWITEM

Func ED_WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg
    Local $iCode = BitShift($wParam, 16)
    Switch $lParam
        Case GUICtrlGetHandle($idInput)
            Switch $iCode
                Case $EN_SETFOCUS
                    ;GUICtrlSetData($hLabel, "Edit has focus")
                    ConsoleWrite("Edit has focus" & @CRLF)
                    _GUICtrlComboBox_ShowDropDown($idComboBox, True)
                    ControlFocus($hGUI, "", $idComboBox)
                Case $EN_KILLFOCUS
                    ;GUICtrlSetData($hLabel, "Edit does not have focus")
                    ConsoleWrite("Edit does not have focus" & @CRLF)
            EndSwitch
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc  ;==>ED_WM_COMMAND

 

 

Posted

Hi, I opened the include file GuiStatusBar.au3 to have a look at the code in  _GUICtrlStatusBar_EmbedControl()

If you look at the code, it simply computes the position and size of the embedded control, relative to the status bar part. At the very end of the function, we find this :

_WinAPI_SetParent($hControl, $hWnd)
_WinAPI_MoveWindow($hControl, $iConX, $iConY, $iConW, $iConH)

So it seems that changing the parent of a control doesn't display the original colors of the control (?)
If we create a simpler example, it confirms this behavior :

#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <WinAPIGdi.au3>
#include <WinAPISysWin.au3>

$hGUI = GUICreate("Test", 340, 200)

$idButton1 = GUICtrlCreateButton("Button 1 line", 30, 10, 80, 30)
; GUICtrlSetStyle(-1, 0) ; for fun, the easiest way to make the preceding button non tabbable

$idButton2 = GUICtrlCreateButton("Button" & @CRLF & "2 lines", 130, 10, 80, 40, $BS_MULTILINE)

$idLabel = GUICtrlCreateLabel("Label here", 230, 10, 80, 50)
$hLabel = GUICtrlGetHandle($idLabel)
GUICtrlSetBkColor(-1, 0xFF0000) ; red... when its parent is $hGUI

GUISetState()

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit

        Case $idButton1
            _WinAPI_SetParent($hLabel, GUICtrlGetHandle($idButton1))
            _WinAPI_MoveWindow($hLabel, 3, 3, 30, 24)

        Case $idButton2
            _WinAPI_SetParent($hLabel, $hGUI)
    EndSwitch
WEnd

1) A click on button 1 changes the parent of the label control : now button 1 is the parent, the label lost its red background color and is displayed at new coords relative to the top left corner of button 1, its new parent.

2) A click on button 2 reverts the parent to the original $hGUI, the red background reappears and the position / size of the label corresponds to the last coords assigned to the label (now relative to the client part of the GUI)

So if someone knows how to force colors to reappear just after a parent change, please indicate how to do it, thanks ! Let's hope there is some kind of redrawing that can solve it. For the record, _WinAPI_SetBkColor didn't make it.

@WildByDesign if noone offers a solution, then maybe you could try to bypass _WinAPI_SetParent and place your control(s) directly at their final coords in the status bar. I tested it by placing a copy of _GUICtrlStatusBar_EmbedControl (renamed) in the script, just to return the coords computed by the function (commenting out the 2 last lines of the function) etc... The colors were here of course.

6 hours ago, WildByDesign said:

I also realized that adding text with labels to the statusbar with _GUICtrlStatusBar_EmbedControl gave better control over fonts, DPI scaling, etc.

So this could mean that you have to use _WinAPI_SetParent after all, to display better controls in your case (?)

Good luck :)

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Posted

Thank you for the deep dive into this issue, @pixelsearch. You always have a way of providing concise code examples to identify whatever it is that needs to be identified.

6 hours ago, pixelsearch said:

So it seems that changing the parent of a control doesn't display the original colors of the control (?)
If we create a simpler example, it confirms this behavior :

Indeed, yes. You've confirmed that the theming issue comes from the _WinAPI_SetParent. That helps narrow things down. I was reading the Remarks section (SetParent function (winuser.h) - Win32 apps | Microsoft Learn) of the msdn info for SetParent. But I honestly don't have a very good understanding of it.

My assumption is that it is essentially undoing the ownerdrawn parts that you had initially helped to create. I tried position the _GUICtrlStatusBar_EmbedControl lines before the ownerdrawn parts section hoping that it might keep the theming created from there, but no luck.

6 hours ago, pixelsearch said:

maybe you could try to bypass _WinAPI_SetParent and place your control(s) directly at their final coords in the status bar. I tested it by placing a copy of _GUICtrlStatusBar_EmbedControl (renamed) in the script, just to return the coords computed by the function (commenting out the 2 last lines of the function) etc... The colors were here of course.

I tried this right now by create a renamed version of that function within my script and commenting out the _WinAPI_SetParent line. I kept the _WinAPI_MoveWindow line to move the controls to the correct locations of the statusbar parts. The only problem is that it put them all at the top of the GUI window but it did space them apart correctly.

I tried manually adding the lower coordinate of the status bar like this: _WinAPI_MoveWindow($hControl, $iConX, 574, $iConW, $iConH)

This did actually place all of the controls to the right locations on the statusbar. Everything appeared perfect, visually. However, for whatever reason, the input box and combobox both became non-functional.

I confirmed with System Information (Window details) that the controls were indeed all exactly where they were intended to be. But the input box was still not clickable on the statusbar when moved manually like this. But when _WinAPI_SetParent was being used, the input/combo controls were functional. So I am not sure what the problem is with this method.

Here is the current example. Run the script as-is and everything works properly, particularly try the Startup Task combo/input in the status bar. Now, at the very bottom of the script, comment out both _WinAPI_SetParent and _WinAPI_MoveWindow and then uncomment the _WinAPI_MoveWindow line that has the 574 coordinate in it. Run the script again and see that the statusbar looks correct visually but notice that the Startup Task combo/input no longer works.

Script:

Spoiler
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <GuiStatusBar.au3>
#include <StaticConstants.au3>
#include <WinAPIGdi.au3>
#include <WinAPIRes.au3>
#include <WinAPISysWin.au3>
#include <WinAPITheme.au3>
#include <WindowsConstants.au3>

#include <ComboConstants.au3>
#include <EditConstants.au3>
#include <GuiComboBox.au3>
#include <FontConstants.au3>

Opt("MustDeclareVars", 1)

Global $hGUI, $g_hSizebox, $g_hOldProc, $g_hStatus, $g_iHeight, $g_aText, $g_aRatioW, $g_iBkColor, $g_iTextColor, $g_hDots
; Global $g_hBrush ; no need, as _GUICtrlStatusBar_SetBkColor() placed after _WinAPI_SetWindowTheme() does the job correctly

Global $idInput, $idComboBox

Example()

;==============================================
Func Example()

    _GDIPlus_Startup()

    Local Const $SBS_SIZEBOX = 0x08, $SBS_SIZEGRIP = 0x10
    Local $iW = 800, $iH = 600
    Dim $g_iBkColor = 0x000000, $g_iTextColor = 0xFFFFFF

    $hGUI = GUICreate("Resize corner", $iW, $iH, -1, -1)
    GUISetBkColor($g_iBkColor)
    GUICtrlSetDefColor(0xffffff)
    GUISetFont(9,  $FW_NORMAL, $GUI_FONTNORMAL, "Segoe UI")

    ;-----------------
    ; Create a sizebox window (Scrollbar class) BEFORE creating the StatusBar control
    $g_hSizebox = _WinAPI_CreateWindowEx(0, "Scrollbar", "", $WS_CHILD + $WS_VISIBLE + $SBS_SIZEBOX, _
        0, 0, 0, 0, $hGUI) ; $SBS_SIZEBOX or $SBS_SIZEGRIP

    ; Subclass the sizebox (by changing the window procedure associated with the Scrollbar class)
    Local $hProc = DllCallbackRegister('ScrollbarProc', 'lresult', 'hwnd;uint;wparam;lparam')
    $g_hOldProc = _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, DllCallbackGetPtr($hProc))

    Local $hCursor = _WinAPI_LoadCursor(0, $OCR_SIZENWSE)
    _WinAPI_SetClassLongEx($g_hSizebox, -12, $hCursor) ; $GCL_HCURSOR = -12

    ; $g_hBrush = _WinAPI_CreateSolidBrush($g_iBkColor)

    ;-----------------
    $g_hStatus = _GUICtrlStatusBar_Create($hGUI, -1, "", $WS_CLIPSIBLINGS) ; ClipSiblings style +++
    Local $aParts[4] = [200, 400, 600, -1]
    If $aParts[Ubound($aParts) - 1] = -1 Then $aParts[Ubound($aParts) - 1] = $iW ; client width size
    _MyGUICtrlStatusBar_SetParts($g_hStatus, $aParts)

    Dim $g_aText[Ubound($aParts)] = ["Part 0", "", "", ""]
    Dim $g_aRatioW[Ubound($aParts)]
    For $i = 0 To UBound($g_aText) - 1
        _GUICtrlStatusBar_SetText($g_hStatus, "", $i, $SBT_OWNERDRAW)
        ; _GUICtrlStatusBar_SetText($g_hStatus, "", $i, $SBT_OWNERDRAW + $SBT_NOBORDERS) ; interesting ?
        $g_aRatioW[$i] = $aParts[$i] / $iW
    Next


    ; to allow the setting of StatusBar BkColor at least under Windows 10
    _WinAPI_SetWindowTheme($g_hStatus, "", "")

    ; Set status bar background color
    _GUICtrlStatusBar_SetBkColor($g_hStatus, $g_iBkColor)

    $g_iHeight = _GUICtrlStatusBar_GetHeight($g_hStatus) + 3 ; change the constant (+3) if necessary
    $g_hDots = CreateDots($g_iHeight, $g_iHeight, 0xFF000000 + $g_iBkColor, 0xFF000000 + $g_iTextColor)

    ;Local $idInput = GUICtrlCreateInput("embed", 0, 0)
    ;Local $hInput = GUICtrlGetHandle($idInput)
    $idComboBox = GUICtrlCreateCombo("Install", 0, 0, 185, 20, $CBS_DROPDOWNLIST)
    GUICtrlSetState(-1, $GUI_HIDE)
    ;Local $hidComboBox = GUICtrlGetHandle($idComboBox)
    ;GUICtrlSetData($idComboBox, "Uninstall")
    $idInput = GUICtrlCreateInput("Startup Task  ▼", 0, 0, 340, 20, $ES_READONLY, 0)
    GUICtrlSetBkColor(-1, 0x000000)
    Local $idPart0 = GUICtrlCreateLabel("Part 0", 0, 0, -1, -1)
    GUICtrlSetColor(-1, 0xffffff)
    Local $idPart1 = GUICtrlCreateLabel("Part 1", 0, 0, -1, -1)
    Local $idPart2 = GUICtrlCreateLabel("Part 2", 0, 0, -1, -1)
    ;Local $hInput = GUICtrlGetHandle($idInput)
    _GUICtrlStatusBar_EmbedControlEx($g_hStatus, 3, GUICtrlGetHandle($idComboBox), 4)
    _GUICtrlStatusBar_EmbedControlEx($g_hStatus, 3, GUICtrlGetHandle($idInput), 4)
    _GUICtrlStatusBar_EmbedControlEx($g_hStatus, 2, GUICtrlGetHandle($idPart2), 4)
    _GUICtrlStatusBar_EmbedControlEx($g_hStatus, 1, GUICtrlGetHandle($idPart1), 4)
    _GUICtrlStatusBar_EmbedControlEx($g_hStatus, 0, GUICtrlGetHandle($idPart0), 4)
    

    GUIRegisterMsg($WM_SIZE, "WM_SIZE")
    GUIRegisterMsg($WM_MOVE, "WM_MOVE")
    GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM")
    GUIRegisterMsg($WM_COMMAND, "ED_WM_COMMAND")

    GUICtrlSendMsg( $idComboBox, $WM_CHANGEUISTATE, 65537, 0 )
    

    GUISetState()

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $idComboBox
                ;GUICtrlSetData($idInput, GUICtrlRead($idComboBox))
                Local $comboread = GUICtrlRead($idComboBox)
                If $comboread = "Install" Then
                    ConsoleWrite("you choose to install task" & @CRLF)
                    GUICtrlSetData($idComboBox, "")
                    GUICtrlSetData($idComboBox, "Uninstall")
                    GUICtrlSetData($idPart2, "installed")
                EndIf
                If $comboread = "Uninstall" Then
                    ConsoleWrite("you choose to uninstall task" & @CRLF)
                    GUICtrlSetData($idComboBox, "")
                    GUICtrlSetData($idComboBox, "Install")
                    GUICtrlSetData($idPart2, "not installed")
                EndIf
        EndSwitch
    WEnd

    _GDIPlus_BitmapDispose($g_hDots)
    _GUICtrlStatusBar_Destroy($g_hStatus)
    _WinAPI_DestroyCursor($hCursor)
    ; _WinAPI_DeleteObject($g_hBrush)
    _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, $g_hOldProc)
    DllCallbackFree($hProc)
    _GDIPlus_Shutdown()
EndFunc   ;==>Example

;==============================================
Func ScrollbarProc($hWnd, $iMsg, $wParam, $lParam) ; Andreik

    If $iMsg = $WM_PAINT Then
        Local $tPAINTSTRUCT
        Local $hDC = _WinAPI_BeginPaint($hWnd, $tPAINTSTRUCT)
        Local $iWidth = DllStructGetData($tPAINTSTRUCT, 'rPaint', 3) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 1)
        Local $iHeight = DllStructGetData($tPAINTSTRUCT, 'rPaint', 4) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 2)
        Local $hGraphics = _GDIPlus_GraphicsCreateFromHDC($hDC)
        _GDIPlus_GraphicsDrawImageRect($hGraphics, $g_hDots, 0, 0, $iWidth, $iHeight)
        _GDIPlus_GraphicsDispose($hGraphics)
        _WinAPI_EndPaint($hWnd, $tPAINTSTRUCT)
        Return 0
    EndIf
    Return _WinAPI_CallWindowProc($g_hOldProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>ScrollbarProc

;==============================================
Func CreateDots($iWidth, $iHeight, $iBackgroundColor, $iDotsColor) ; Andreik

    Local $iDotSize = Int($iHeight / 10)
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    Local $hBrush = _GDIPlus_BrushCreateSolid($iDotsColor)
    _GDIPlus_GraphicsClear($hGraphics, $iBackgroundColor)
    Local $a[6][2] = [[2,6], [2,4], [2,2], [4,4], [4,2], [6,2]]
    For $i = 0 To UBound($a) - 1
        _GDIPlus_GraphicsFillRect($hGraphics, $iWidth - $iDotSize * $a[$i][0], $iHeight - $iDotSize * $a[$i][1], $iDotSize, $iDotSize, $hBrush)
    Next
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hGraphics)
    Return $hBitmap
EndFunc   ;==>CreateDots

;==============================================
Func _MyGUICtrlStatusBar_SetParts($hWnd, $aPartEdge) ; Pixelsearch

    If Not IsArray($aPartEdge) Then Return False
    Local $iParts = UBound($aPartEdge)
    Local $tParts = DllStructCreate("int[" & $iParts & "]")
    For $i = 0 To $iParts - 1
        DllStructSetData($tParts, 1, $aPartEdge[$i], $i + 1)
    Next
    DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $hWnd, "uint", $SB_SETPARTS, "wparam", $iParts, "struct*", $tParts)
    _GUICtrlStatusBar_Resize($hWnd)
    Return True
EndFunc   ;==>_MyGUICtrlStatusBar_SetParts

;==============================================
Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam) ; Pixelsearch
    #forceref $iMsg, $wParam, $lParam

    If $hWnd = $hGUI Then
        Local Static $bIsSizeBoxShown = True
        Local $aSize = WinGetClientSize($hGUI)
        Local $aGetParts = _GUICtrlStatusBar_GetParts($g_hStatus)
        Local $aParts[$aGetParts[0]]
        For $i = 0 To $aGetParts[0] - 1
            $aParts[$i] = Int($aSize[0] * $g_aRatioW[$i])
        Next
        If BitAND(WinGetState($hGUI), $WIN_STATE_MAXIMIZED) Then
            _GUICtrlStatusBar_SetParts($g_hStatus, $aParts) ; set parts until GUI right border (AutoIt function forces it)
            _WinAPI_ShowWindow($g_hSizebox, @SW_HIDE)
            $bIsSizeBoxShown = False
        Else
            If $g_aRatioW[UBound($aParts) -1] <> 1 Then $aParts[UBound($aParts) -1] = $aSize[0] - $g_iHeight ; right size of right part stuck on sizebox (no gap)
            _MyGUICtrlStatusBar_SetParts($g_hStatus, $aParts) ; set parts until sizebox (see preceding line) or until GUI right border
            WinMove($g_hSizebox, "", $aSize[0] - $g_iHeight, $aSize[1] - $g_iHeight, $g_iHeight, $g_iHeight)
            If Not $bIsSizeBoxShown Then
                _WinAPI_ShowWindow($g_hSizebox, @SW_SHOW)
                $bIsSizeBoxShown = True
            EndIf
        EndIf
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_SIZE

;==============================================
Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam

    If $hWnd = $hGUI Then
        _WinAPI_RedrawWindow($g_hSizebox)
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE

;==============================================
Func WM_DRAWITEM($hWnd, $iMsg, $wParam, $lParam) ; Kafu
    #forceref $hWnd, $iMsg, $wParam

    Local Static $tagDRAWITEM = "uint CtlType;uint CtlID;uint itemID;uint itemAction;uint itemState;hwnd hwndItem;handle hDC;long rcItem[4];ulong_ptr itemData"
    Local $tDRAWITEM = DllStructCreate($tagDRAWITEM, $lParam)
    If $tDRAWITEM.hwndItem <> $g_hStatus Then Return $GUI_RUNDEFMSG ; only process the statusbar

    Local $itemID = $tDRAWITEM.itemID ; status bar part number : 0 if simple bar (or 0, 1, 2... if multi parts)
    Local $hDC = $tDRAWITEM.hDC
    Local $tRect = DllStructCreate("long left;long top;long right;long bottom", DllStructGetPtr($tDRAWITEM, "rcItem"))
    ; _WinAPI_FillRect($hDC, DllStructGetPtr($tRect), $g_hBrush) ; backgound color
    _WinAPI_SetTextColor($hDC, $g_iTextColor) ; text color
    _WinAPI_SetBkMode($hDC, $TRANSPARENT)
    DllStructSetData($tRect, "top", $tRect.top + 1)
    DllStructSetData($tRect, "left", $tRect.left + 1)
    _WinAPI_DrawText($hDC, $g_aText[$itemID], $tRect, $DT_LEFT)

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_DRAWITEM

Func ED_WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg
    Local $iCode = BitShift($wParam, 16)
    Switch $lParam
        Case GUICtrlGetHandle($idInput)
            Switch $iCode
                Case $EN_SETFOCUS
                    ;GUICtrlSetData($hLabel, "Edit has focus")
                    ConsoleWrite("Edit has focus" & @CRLF)
                    _GUICtrlComboBox_ShowDropDown($idComboBox, True)
                    ControlFocus($hGUI, "", $idComboBox)
                Case $EN_KILLFOCUS
                    ;GUICtrlSetData($hLabel, "Edit does not have focus")
                    ConsoleWrite("Edit does not have focus" & @CRLF)
            EndSwitch
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc  ;==>ED_WM_COMMAND

; #FUNCTION# ====================================================================================================================
; Author ........: Paul Campbell (PaulIA)
; Modified.......:
; ===============================================================================================================================
Func _GUICtrlStatusBar_EmbedControlEx($hWnd, $iPart, $hControl, $iFit = 4)
    Local $aRect = _GUICtrlStatusBar_GetRect($hWnd, $iPart)
    Local $iBarX = $aRect[0]
    Local $iBarY = $aRect[1]
    Local $iBarW = $aRect[2] - $iBarX
    Local $iBarH = $aRect[3] - $iBarY

    Local $iConX = $iBarX
    Local $iConY = $iBarY
    Local $iConW = _WinAPI_GetWindowWidth($hControl)
    Local $iConH = _WinAPI_GetWindowHeight($hControl)

    If $iConW > $iBarW Then $iConW = $iBarW
    If $iConH > $iBarH Then $iConH = $iBarH
    Local $iPadX = ($iBarW - $iConW) / 2
    Local $iPadY = ($iBarH - $iConH) / 2
    If $iPadX < 0 Then $iPadX = 0
    If $iPadY < 0 Then $iPadY = 0

    If BitAND($iFit, 1) = 1 Then $iConX = $iBarX + $iPadX
    If BitAND($iFit, 2) = 2 Then $iConY = $iBarY + $iPadY
    If BitAND($iFit, 4) = 4 Then
        $iPadX = _GUICtrlStatusBar_GetBordersRect($hWnd)
        $iPadY = _GUICtrlStatusBar_GetBordersVert($hWnd)
        $iConX = $iBarX
        If _GUICtrlStatusBar_IsSimple($hWnd) Then $iConX += $iPadX
        $iConY = $iBarY + $iPadY
        $iConW = $iBarW - ($iPadX * 2)
        $iConH = $iBarH - ($iPadY * 2)
    EndIf

    _WinAPI_SetParent($hControl, $hWnd)
    _WinAPI_MoveWindow($hControl, $iConX, $iConY, $iConW, $iConH)
    ;_WinAPI_MoveWindow($hControl, $iConX, 574, $iConW, $iConH)
EndFunc   ;==>_GUICtrlStatusBar_EmbedControlEx

 

 

Posted

I was testing a few things to see what options I have just in case this doesn't end up working. So I have a few observations.

If I add the following right before GUISetState:

_WinAPI_SetWindowTheme($g_hStatus, "Explorer")

It themes the statusbar embedded parts well, including and importantly the combobox drop-down. Now, of course this destroys the nice ownerdrawn parts which therefore destroys the lovely dark statusbar.

So I thought about trying the built-in Windows 10/11 statusbar dark mode. It looks like the screenshot here: 

Previously, you would enable dark mode statusbar with the following:

_WinAPI_SetWindowTheme($g_hStatus, "DarkMode_Explorer", "Explorer")

It used to work well, but of course with the thicker white border lines which were hard on the eyes.

Light mode status bar used to be set with:

_WinAPI_SetWindowTheme($g_hStatus, "Explorer", "Explorer")

For whatever reason, on the latest stable channel Windows 11 24H2 with latest updates, both of these fail now when they used to work. The last argument "Explorer" is causing it to completely remove the statusbar theme now. It is essentially now equal to what you get with: _WinAPI_SetWindowTheme($g_hStatus, "", "")

So now on latest Windows 11, you have to use it like this:

_WinAPI_SetWindowTheme($g_hStatus, "DarkMode_Explorer")
_WinAPI_SetWindowTheme($g_hStatus, "Explorer")

However, both of those enable light mode themed statusbar only.

It would seem that DarkMode statusbar has been removed from the latest Windows 11 updates. Either that, or the latest Windows 11 updates have completely changed how SetWindowTheme functions now.

Posted (edited)

I have also tried the unrestricted SetWindowTheme (_WinAPI_SetWindowTheme_unr) function from @argumentum and it also fails (or behaves differently) on latest Windows 11 stable channel. 24H2 Build 26100 .4652

Edited by WildByDesign
Posted (edited)
3 hours ago, WildByDesign said:

SetWindowTheme (_WinAPI_SetWindowTheme_unr) function from @argumentum and it also fails (or behaves differently) on latest Windows 11 stable channel. 24H2 Build 26100 .4652

My most awesome "_WinAPI_SetWindowTheme_unr()" is "_WinAPI_SetWindowTheme()" without the "child proofing", the rest is the standard WinAPI.
If on 26100 .4652 things don't work, .. tell M$ ? :D

The simple approach I gave you in the beginning was "fake it" and that's, what is in my vast ( or not ) knowledge of GUIs 🤯

Edited by argumentum
English

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

Posted
33 minutes ago, argumentum said:

The simple approach I gave you in the beginning was "fake it" and that's, what is in my vast ( or not ) knowledge of GUIs

This is probably the best and maybe only approach for me now. I want to be able to release the GUI soon since it is very close to being done. This is really the main thing holding it back now.

I am going to look over your previous “fake” status bar functions from the other thread now and get them to work with what I currently have.

Posted (edited)
38 minutes ago, argumentum said:

tell M$ ? :D

This I agree with 100%. This should be the right way to go. However, after many past experiences of reporting things to M$, it is comparable to having a conversation with a goat. :)

Edited by WildByDesign
  • Solution
Posted
14 hours ago, WildByDesign said:

This did actually place all of the controls to the right locations on the statusbar. Everything appeared perfect, visually. However, for whatever reason, the input box and combobox both became non-functional.

I tried to add these 2 controls (combo & input box) to my script found in this post with good results, on a maximizable / resizable GUI :

Statusbarwithcoloredcontrolsinit.png.6e23bd1239e4379c558616cf19242afd.png

These are the lines modified / added to the script, so you can try them too :

1) Lines modified :

; Local $iW = 300, $iH = 100
Local $iW = 600, $iH = 200

; Local $aParts[3] = [90, 180, 280]
Local $aParts[3] = [200, 400, 580]

; Local $idChangeText = GUICtrlCreateLabel("Change Text", 110, 25, 80, 30, $SS_CENTER + $SS_CENTERIMAGE), $iInc
Local $idChangeText = GUICtrlCreateLabel("Change Text", $iW / 2 - 40, 50, 80, 30, $SS_CENTER + $SS_CENTERIMAGE), $iInc

2) Lines added :

#include <ComboConstants.au3>
...
Local $idComboBox = GUICtrlCreateCombo("Install", 100, 180, 100, 18, $CBS_DROPDOWNLIST)
GUICtrlSetData($idComboBox, "Uninstall")
GUICtrlSetBkColor(-1, 0xFF0000) ; red
_WinAPI_SetParent(GUICtrlGetHandle($idComboBox), $g_hGUI)

Local $idInput = GUICtrlCreateInput("Input in part 1", 300, 181, 100, 18)
GUICtrlSetBkColor(-1, 0x00FF00) ; green
_WinAPI_SetParent(GUICtrlGetHandle($idInput), $g_hGUI)

* Please create $idComboBox and $idInput just after the label $idChangeText
* IIRC your StatusBar height got a higher value, so you'll probably have to lower a bit the Y coord of both controls
* Lucky me, I tried the _WinAPI_SetParent() on each control (combo & input), indicating... the GUI as parent and it "woke" them up both. Without this instruction, they didn't react, as you indicated.

Hope it will work on your side, fingers crossed :)

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Posted
23 minutes ago, pixelsearch said:

Hope it will work on your side,

image.png.dc3f53a39d28a020c5331adb8c81fd9e.png   

image.png.44d09276a97b6a791d6b1040c587c8ed.png

...so, yes. It should work everywhere.
When he claimed that something changed drastically ( not sure what ) I jumped into getting the latest to test and not make a fool of my self calling Bill to give him a piece of my mind !. Fortunately, I didn't have to 😇

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

Posted (edited)
9 hours ago, pixelsearch said:

Lucky me, I tried the _WinAPI_SetParent() on each control (combo & input), indicating... the GUI as parent and it "woke" them up both. Without this instruction, they didn't react, as you indicated.

Hope it will work on your side, fingers crossed

This did it. This solved my problem. It's funny, too, because _WinAPI_SetParent() was problematic at one point causing issues by destroying the theming. And now here comes _WinAPI_SetParent() to save the day by restoring the lovely theming. It blows my mind! :)

Thank you so much for your help and for your time with this. I appreciate it, as always. You've saved me a few times now already.

Now I can move on to the not-so-fun DPI awareness control sizing stuff.

Edited by WildByDesign
Posted (edited)
1 hour ago, BugFix said:

I've created a label based solution for a simple status bar. May be it's interesting 4u.

Your UDF is a nice option. Thanks for letting me know about it because I was not aware. It deserves more attention.

By the way, is it possible to obtain a position on each of the parts?

The reason why I ask is because I would like to be able to swap one or two of the parts with an input or combo control.

Edited by WildByDesign
Posted
2 hours ago, BugFix said:

I've created a label based solution for a simple status bar.

Very interesting, thanks for sharing.

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Posted
On 7/11/2025 at 3:55 PM, WildByDesign said:

By the way, is it possible to obtain a position on each of the parts?

The reason why I ask is because I would like to be able to swap one or two of the parts with an input or combo control.

By default, embedding other controls in my status bar is not provided. But some thoughts on this:

The function "_StatusbarCreate" returns an array that contains, among other things, the IDs of all parts. The ID can be used to query the part's rectangle. One option would be to hide the original label and overlay it with another control at the determined position. Some optical corrections may still be necessary to achieve a uniform appearance. That is the solution I would choose. Good luck with your attempts.

Best Regards BugFix  

Posted

I just found out the hard way that the _GUICtrlStatusBar_EmbedControl function has a terrible bug that renders it essentially useless.

If you use the same example from (Function _GUICtrlStatusBar_EmbedControl), if you run the example, minimize the GUI and restore the GUI, the majority of the parts disappear and/or move to another area of the GUI.

I wish that I noticed this a few days ago before I went all-in on it.

2 hours ago, BugFix said:

The function "_StatusbarCreate" returns an array that contains, among other things, the IDs of all parts. The ID can be used to query the part's rectangle. One option would be to hide the original label and overlay it with another control at the determined position. Some optical corrections may still be necessary to achieve a uniform appearance. That is the solution I would choose. Good luck with your attempts.

Thank you for this information. The fact that your UDF function provides an array with all of those details is fantastic. This might just save me from the broken statusbar situation that I currently have right now. I'm going to try your UDF now.

Posted
59 minutes ago, WildByDesign said:

just found out the hard way that the _GUICtrlStatusBar_EmbedControl function has a terrible bug that renders it essentially useless.

If you use the same example from (Function _GUICtrlStatusBar_EmbedControl), if you run the example, minimize the GUI and restore the GUI, the majority of the parts disappear and/or move to another area of the GUI.

I noticed that a couple of days ago but did not report, after we saw the issue in _GUICtrlStatusBar_EmbedControl which did not let you embed controls in your script, because of  _WinAPI_SetParent() which change the parent of the embedded controls, then their background colors were not the ones you wanted.

I'm sure other examples in the help file would need the same fix, e.g. to take care of the messages WM_SIZE / WM_MOVE when the GUI is resized, minimized etc... and calculate the position of these controls during the registered messages functions.

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

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