Jump to content

Recommended Posts

Posted

I wanted to create a dark mode menubar without requiring ModernMenuRaw UDF which uses Ownerdrawn menubar. The problem with Ownerdrawn menubar is that it always leaves a single pixel white line across the entire bottom of the menubar which is part of the non-client area and therefore no perfect method for permanently removing it.

Screenshot:

image.png.7515f0e6055ebfeb25fe5281b71850b1.png

Code:

#NoTrayIcon
#include <GuiMenu.au3>
#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>
#include <StaticConstants.au3>
#include <Array.au3>

#include "_WinAPI_DPI.au3"
#include "GuiFlatButton_menu.au3"

Opt("GUIOnEventMode", 1)

Global $iDPI1, $iDPI2, $hGUI
Global $aWinPos, $aClientSize, $iCaptionHeight
Global $iMenubarBk = 0x202020
Global $iMenubarHov = 0x404040
Global $iMenubarSel = 0x404040
Global $iPadding = 20
Global $hFileMenu, $hEditMenu, $hHelpMenu, $iMenuHeight

Global $iDPI = _WinAPI_SetDPIAwareness(), $iDPI_def = 96
If $iDPI = 0 Then Exit MsgBox($MB_ICONERROR, "ERROR", "Unable to set DPI awareness!!!", 10)
$iDPI1 = $iDPI / $iDPI_def
$iDPI2 = $iDPI_def / $iDPI

;GuiFlatButton_SetDefaultColors(0x000000, 0xFFFFFF, 0x000000)
    ;set default colors of future buttons
    Local $aColorsEx = _
        [$iMenubarBk, 0xFFFFFF, $iMenubarBk, _  ; normal    : Background, Text, Border
        $iMenubarBk, 0xFFFFFF, $iMenubarBk, _   ; focus     : Background, Text, Border
        $iMenubarHov, 0xFFFFFF, $iMenubarHov, _ ; hover     : Background, Text, Border
        $iMenubarSel, 0xFFFFFF, $iMenubarSel]       ; selected  : Background, Text, Border
    GuiFlatButton_SetDefaultColorsEx($aColorsEx)

;DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext" , "HWND", "DPI_AWARENESS_CONTEXT" -4)

_GUI_Start()
Func _GUI_Start()
    $hFileMenu = _GUICtrlMenu_CreatePopup()
    _GUICtrlMenu_InsertMenuItem($hFileMenu, 0, "Exit", 1)

    $hEditMenu = _GUICtrlMenu_CreatePopup()
    _GUICtrlMenu_InsertMenuItem($hEditMenu, 0, "Undo", 1)
    _GUICtrlMenu_InsertMenuItem($hEditMenu, 1, "Copy", 2)
    _GUICtrlMenu_InsertMenuItem($hEditMenu, 2, "Paste", 3)

    $hHelpMenu = _GUICtrlMenu_CreatePopup()
    _GUICtrlMenu_InsertMenuItem($hHelpMenu, 0, "View Help", 1)
    _GUICtrlMenu_InsertMenuItem($hHelpMenu, 1, "About Program", 2)

    $hGUI = GUICreate("Dark Menubar - concept")
    GUISetBkColor(0x101010)
    ;GuiDarkmodeApply($hGUI)
    DarkMode($hGUI, True)
    GUISetFont(10, $FW_NORMAL, -1, "Segoe UI")
    GUISetOnEvent($GUI_EVENT_CLOSE, "SpecialEvents")
    $aWinPos = WinGetPos($hGUI)
    $aClientSize = WinGetClientSize($hGUI)

    $idFileMenu = GuiFlatButton_Create("File", -2, -2, -1, -1, $SS_CENTER)
    GUICtrlSetOnEvent(-1, "idFileMenu")
    ; get initial position
    $aPos = ControlGetPos($hGUI, "", $idFileMenu)
    GuiFlatButton_SetPos($idFileMenu, $aPos[0], $aPos[1], $aPos[2] + $iPadding, $aPos[3])
    ; get position after padding
    $aPos = ControlGetPos($hGUI, "", $idFileMenu)

    $iMenuHeight = $aPos[3] - 2

    $idMenuBar = GUICtrlCreateLabel(" ", 0, 0, $aClientSize[1], $aPos[3] - 2, $WS_CLIPSIBLINGS)
    GUICtrlSetBkColor(-1, $iMenubarBk)

    $idEditMenu = GuiFlatButton_Create("Edit", $aPos[0] + $aPos[2], -2, -1, -1, $SS_CENTER)
    GUICtrlSetOnEvent(-1, "idEditMenu")
    ; get initial position
    $aPos = ControlGetPos($hGUI, "", $idEditMenu)
    GuiFlatButton_SetPos($idEditMenu, $aPos[0], $aPos[1], $aPos[2] + $iPadding, $aPos[3])
    ; get position after padding
    $aPos = ControlGetPos($hGUI, "", $idEditMenu)

    $idHelpMenu = GuiFlatButton_Create("Help", $aPos[0] + $aPos[2], -2, -1, -1, $SS_CENTER)
    GUICtrlSetOnEvent(-1, "idHelpMenu")
    ; get initial position
    $aPos = ControlGetPos($hGUI, "", $idHelpMenu)
    GuiFlatButton_SetPos($idHelpMenu, $aPos[0], $aPos[1], $aPos[2] + $iPadding, $aPos[3])
    ; get position after padding
    $aPos = ControlGetPos($hGUI, "", $idHelpMenu)

    $aCtrlPos = ControlGetPos($hGUI, "", $idFileMenu)
    ;GUISetState(@SW_SHOW)

    $iCaptionHeight = _TitleBarSize($hGUI)

    ConsoleWrite("titlebar height: " & $iCaptionHeight & @CRLF)

    GUISetState(@SW_SHOW)

    ; Just idle around
    While 1
        Sleep(1000)
    WEnd
EndFunc

Func idFileMenu()
    ; need to redo sizes here in case window moved
    $aWinPos = WinGetPos($hGUI)
    $MenuResult = _GUICtrlMenu_TrackPopupMenu($hFileMenu, $hGUI, $aWinPos[0] + 2, $aWinPos[1] + $iCaptionHeight + $iMenuHeight, 1, 1, 2)
    Switch $MenuResult
        Case 0
            ConsoleWrite("menu has been clicked away" & @CRLF)
        Case 1
            Exit
    EndSwitch
EndFunc

Func idEditMenu()
    ; need to redo sizes here in case window moved
    $aWinPos = WinGetPos($hGUI)
    $MenuResult = _GUICtrlMenu_TrackPopupMenu($hEditMenu, $hGUI, $aWinPos[0] + 2 + 52, $aWinPos[1] + $iCaptionHeight + $iMenuHeight, 1, 1, 2)
    Switch $MenuResult
        Case 0
            ConsoleWrite("menu has been clicked away" & @CRLF)
        Case 1
            ConsoleWrite("You clicked on Undo" & @CRLF)
        Case 2
            ConsoleWrite("You clicked on Copy" & @CRLF)
        Case 3
            ConsoleWrite("You clicked on Paste" & @CRLF)
    EndSwitch
EndFunc

Func idHelpMenu()
    ; need to redo sizes here in case window moved
    $aWinPos = WinGetPos($hGUI)
    $MenuResult = _GUICtrlMenu_TrackPopupMenu($hHelpMenu, $hGUI, $aWinPos[0] + 2 + 52 + 52, $aWinPos[1] + $iCaptionHeight + $iMenuHeight, 1, 1, 2)
    Switch $MenuResult
        Case 0
            ConsoleWrite("menu has been clicked away" & @CRLF)
        Case 1
            ConsoleWrite("You clicked on View Help" & @CRLF)
        Case 2
            ConsoleWrite("You clicked on About Program" & @CRLF)
            MsgBox(0, "About Program", "Cool Program Name" & @CRLF & @CRLF & "Version: 1.0" & @CRLF & "Created by: WildByDesign")
    EndSwitch
EndFunc

Func SpecialEvents()
    Select
        Case @GUI_CtrlId = $GUI_EVENT_CLOSE
            Exit
    EndSelect
EndFunc

Func _TitleBarSize($hWnd)
        Static $iFrame = _WinAPI_GetSystemMetrics( 32)
        Static $iBorder = _WinAPI_GetSystemMetrics( 5)
        $tWinRect = _WinAPI_GetWindowRect( $hWnd)
        $tClientRect = _WinAPI_GetClientRect( $hWnd)
        $iWinHeight = DllStructGetData( $tWinRect, "Bottom") - DllStructGetData( $tWinRect, "Top")
        $iClientHeight = DllStructGetData( $tClientRect, "Bottom") - DllStructGetData( $tClientRect, "Top")
        $iTitleBarSize = ($iWinHeight - $iClientHeight - $iFrame - $iBorder) * $iDPI1
        Return $iTitleBarSize
EndFunc

;--------------------------------------------------------------------------------------------------------------------------------
; https://www.autoitscript.com/forum/topic/211475-darkmode-udf-for-autoits-win32guis/#comment-1530103
;--------------------------------------------------------------------------------------------------------------------------------
Func DarkMode($hGUI, $bDarkMode = True)                               ; DarkMode

    Local Enum $DWMWA_USE_IMMERSIVE_DARK_MODE = (@OSBuild <= 18985) ? 19 : 20
    ;ConsoleWrite("$DWMWA_USE_IMMERSIVE_DARK_MODE=" & $DWMWA_USE_IMMERSIVE_DARK_MODE & @CRLF)
    ;       DWMWA_USE_IMMERSIVE_DARK_MODE ; https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
    ;       Use with DwmSetWindowAttribute. Allows the window frame for this window to be drawn in dark mode colors when the dark mode system setting is enabled.
    ;       For compatibility reasons, all windows default to light mode regardless of the system setting.
    ;       The pvAttribute parameter points to a value of type BOOL. TRUE to honor dark mode for the window, FALSE to always use light mode.
    ;       This value is supported starting with Windows 11 Build 22000.

    Local $iRet = _WinAPI_DwmSetWindowAttribute_unr($hGUI, $DWMWA_USE_IMMERSIVE_DARK_MODE, $bDarkMode)
    If Not $iRet Then Return SetError(1, 0, -1)

    ;_SetCtrlColorMode($Button_OK, $bDarkMode)
    ;_SetCtrlColorMode($Button_copy, $bDarkMode)
    _SetCtrlColorMode($hGUI, $bDarkMode)
    ;_WinAPI_SetWindowTheme_unr(GUICtrlGetHandle($ChbxDrkMode), 0, 0) ; this control needs the theme

EndFunc   ;==>DarkMode
;--------------------------------------------------------------------------------------------------------------------------------
Func _SetCtrlColorMode($hWnd, $bDarkMode = True, $sName = Default)    ; 'Explorer', 'CFD', 'DarkMode_ItemsView', etc.
    If $sName = Default Then $sName = $bDarkMode ? 'DarkMode_Explorer' : 'Explorer'
    $bDarkMode = Not Not $bDarkMode ; https://www.vbforums.com/showthread.php?900444-Windows-10-Dark-Mode-amp-VB6-apps
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)
    Local Enum $eDefault, $eAllowDark, $eForceDark, $eForceLight, $eMax ; enum PreferredAppMode
    DllCall('uxtheme.dll', 'bool', 133, 'hwnd', $hWnd, 'bool', $bDarkMode) ; fnAllowDarkModeForWindow = 133
    DllCall('uxtheme.dll', 'int', 135, 'int', ($bDarkMode ? $eForceDark : $eForceLight)) ; fnAllowDarkModeForApp = 135
    _WinAPI_SetWindowTheme_unr($hWnd, $sName) ; https://www.autoitscript.com/forum/index.php?showtopic=211475&view=findpost&p=1530103
    DllCall('uxtheme.dll', 'none', 104) ; fnRefreshImmersiveColorPolicyState = 104  ; not needed ?
    _SendMessage($hWnd, $WM_THEMECHANGED, 0, 0) ; not needed ?
EndFunc   ;==>_SetCtrlColorMode
;--------------------------------------------------------------------------------------------------------------------------------
Func _WinAPI_SetWindowTheme_unr($hWnd, $sName = Null, $sList = Null)  ; #include <WinAPITheme.au3> ; unthoughtful unrestricting mod.
    ;Causes a window to use a different set of visual style information than its class normally uses
    Local $sResult = DllCall('UxTheme.dll', 'long', 'SetWindowTheme', 'hwnd', $hWnd, 'wstr', $sName, 'wstr', $sList)
    If @error Then Return SetError(@error, @extended, 0)
    If $sResult[0] Then Return SetError(10, $sResult[0], 0)
    Return 1
EndFunc   ;==>_WinAPI_SetWindowTheme_unr
;--------------------------------------------------------------------------------------------------------------------------------
Func _WinAPI_DwmSetWindowAttribute_unr($hWnd, $iAttribute, $iData)    ; #include <WinAPIGdi.au3> ; unthoughtful unrestricting mod.
    ;Sets the value of the specified attributes for non-client rendering to apply to the window
    Local $aCall = DllCall('dwmapi.dll', 'long', 'DwmSetWindowAttribute', 'hwnd', $hWnd, 'dword', $iAttribute, _
            'dword*', $iData, 'dword', 4)
    If @error Then Return SetError(@error, @extended, 0)
    If $aCall[0] Then Return SetError(10, $aCall[0], 0)
    Return 1
EndFunc   ;==>_WinAPI_DwmSetWindowAttribute_unr
;--------------------------------------------------------------------------------------------------------------------------------

UDF's Required:

_WinAPI_DPI.au3GuiFlatButton_menu.au3

 

If anybody has any interest in this and wants to collaborate, I would love that.

One thing that is lacking is the ability to easily move from menu to menu when you have one already open. Eg. You have a menu open and hover over the other menus. In most programs, it would automatically open those. I am sure that it is possible to do this but it is beyond my skill level.

Posted

I ended up using this same technique of using _GUICtrlMenu_TrackPopupMenu to control the exact placement of a context/popup menu for a statusbar item recently as well with excellent results.

Link: https://github.com/WildByDesign/ImmersiveUX/blob/main/ImmersiveUX.au3#L865

Measurement was easier for that though because it was the bottom of the GUI and so titlebar did not need to be calculated in this case.

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