WildByDesign Posted March 5 Author Posted March 5 (edited) @Kanashius I was just looking over some of the changes and noticed the comment: ; delete the last item later to avoid the menu to flicker in light mode Is it possible that that could be the cause of the weird File menu issue that is making the other menus disappear? That reminds me. I personally think that we should also subclass the menu in light mode as well since that is what we were doing when using ModernMenuRaw. The reason for subclassing it in light mode as well made it easier for switching back and forth between light mode and dark mode. This way, the brushes just change color and we don't have to delete brushes and stop the subclassing when going to light mode. I haven't done any of the colors for light mode yet though. So there is still more work to do for that. By the way, do you have any idea how to have more control over the top menu font size? EDIT: I just pushed another commit that allows changing the font size. I have tested it up to 175% scaling and nothing was cut off on the menu text here. If you have a monitor with 200% scaling or higher, could you please test for top menu text cut off? Edited March 5 by WildByDesign
Kanashius Posted Thursday at 11:25 AM Posted Thursday at 11:25 AM 11 hours ago, WildByDesign said: That's a really great idea. I like it. And it makes sense to avoid any other users' usage of GUIRegisterMsg. I am not very familiar with the method that you suggest about registering callbacks. I am a bit worried that if I make a mistake with a change like that, it could be significant. Would you be willing to help convert to this callback method? ; create the callback once Local $hProc = DllCallbackRegister('__GUIDarkMode__WinProc', 'ptr', 'hwnd;uint;wparam;lparam') ; add it to every window using the udf Local $hPrevProc = _WinAPI_SetWindowLong($hGui, -4, DllCallbackGetPtr($hProc)) ; when the gui is no longer using the udf, remove the callback _WinAPI_SetWindowLong($hGui, -4, $hPrevProc) ; free callback at exit DllCallbackFree($hProc) Func __GUIDarkMode__WinProc($hWnd, $iMsg, $iwParam, $ilParam) Switch $iMsg Case $WM_NOTIFY ; ... Case $WM_MEASUREITEM __GUIDarkMode__WM_MEASUREITEM($hwnd, $iMsg, $iwParam, $ilParam) Case $WM_DRAWITEM __GUIDarkMode__WM_DRAWITEM($hwnd, $iMsg, $iwParam, $ilParam) EndSwitch Return _WinAPI_CallWindowProc($hPrevProc, $hWnd, $iMsg, $iwParam, $ilParam) EndFunc I think, the code explains everything important to do. The WM_Message events are essentially chained function calls and we are inserting our function into that chain. For AutoIt purposes: The Gui sends the event, it goes through the function chain and at the end it hits the function registered with GuiRegisterMsg. In this example, the __GUIDarkMode__WinProc will be called by the previous message handler and it calls the next one before/when returning. You can also interrupt this chain with e.g. "Return 1". Then the default AutoIt-Processing will not happen. This is normally done, if you handled an event and do not want others to handle them as well. Example: When hovering over a control you change the cursor, but AutoIt overwrites that change with their own cursor change. This function chain starts with the gui sending the event, so that is where we are registering our callback function. That is also, why we deregister there, when we no longer want to handle events, so we remove our callback from the chain. WildByDesign 1 My Website: Kanashius Webside (Some of my Programs you can find there)
WildByDesign Posted Thursday at 11:53 AM Author Posted Thursday at 11:53 AM (edited) 28 minutes ago, Kanashius said: I think, the code explains everything important to do. Thank you. And I agree, the code example does explain itself. Your description also helps my understanding of how it all works. I spent some time on it early this morning and it ended up failing. The entire GUI is blank and there is not even a frame, titlebar or control buttons. I was very careful to ensure that the parameters matched up correctly. But all of this failed in the end. I apologize, but I think that a change like this is not something that I can do personally. Maybe there is some clear mistakes that you can spot in my changes below. I will show my relevant changes to this: expandcollapse popup; Create Callback Global $hMenuProc = DllCallbackRegister('__GUIDarkMenu_WinProc', 'ptr', 'hwnd;uint;wparam;lparam') Global $hPrevMenuProc = __WinAPI_SetWindowLong($hWnd, -4, DllCallbackGetPtr($hMenuProc)) Func __GUIDarkMenu_WinProc($hWnd, $iMsg, $iwParam, $ilParam) Switch $iMsg Case $WM_WINDOWPOSCHANGED __GUIDarkMode__WM_WINDOWPOSCHANGED($hWnd, $iMsg, $iwParam, $ilParam) Case $WM_ACTIVATE __GUIDarkMode__WM_ACTIVATE($hWnd, $iMsg, $iwParam, $ilParam) Case $WM_MEASUREITEM __GUIDarkMode__WM_MEASUREITEM($hWnd, $iMsg, $iwParam, $ilParam) Case $WM_DRAWITEM __GUIDarkMode__WM_DRAWITEM($hWnd, $iMsg, $iwParam, $ilParam) EndSwitch EndFunc Func __GUIDarkMode__WM_WINDOWPOSCHANGED($hWnd, $iMsg, $iwParam, $ilParam) #forceref $iMsg, $iwParam, $ilParam If $hWnd <> $hGUI Then Return $GUI_RUNDEFMSG _drawUAHMenuNCBottomLine($hWnd) Return $GUI_RUNDEFMSG EndFunc ;==>__GUIDarkMode__WM_WINDOWPOSCHANGED Func __GUIDarkMode__WM_ACTIVATE($hWnd, $iMsg, $iwParam, $ilParam) #forceref $iMsg, $iwParam, $ilParam If $hWnd <> $hGUI Then Return $GUI_RUNDEFMSG _drawUAHMenuNCBottomLine($hWnd) Return $GUI_RUNDEFMSG EndFunc ;==>__GUIDarkMode__WM_ACTIVATE Func __GUIDarkMode__WM_DRAWITEM($hWnd, $iMsg, $iwParam, $ilParam) #forceref $iMsg, $iwParam Local Const $SM_CXDLGFRAME = 7 Local Const $DEFAULT_GUI_FONT = 17 Local $tagDRAWITEM = "uint CtlType;uint CtlID;uint itemID;uint itemAction;uint itemState;ptr hwndItem;handle hDC;" & _ "long left;long top;long right;long bottom;ulong_ptr itemData" Local $t = DllStructCreate($tagDRAWITEM, $ilParam) If Not IsDllStruct($t) Then Return $GUI_RUNDEFMSG If $t.CtlType <> $ODT_MENU Then Return $GUI_RUNDEFMSG Local $hDC = $t.hDC Local $left = $t.left Local $top = $t.top Local $right = $t.right Local $bottom = $t.bottom Local $state = $t.itemState Local $itemID = $t.itemID ; convert itemID to position Local $iPos = -1 For $i = 0 To UBound($g_aMenuText) - 1 If $itemID = $g_aMenuText[$i][0] Then $iPos = $i ExitLoop EndIf Next If $iPos < 0 Then $iPos = $itemID If $iPos < 0 Or $iPos >= UBound($g_aMenuText) Then $iPos = 0 Local $sText = $g_aMenuText[$iPos][1] $sText = StringReplace($sText, "&", "") ; Colors Local $clrBG = _ColorToCOLORREF($COLOR_MENU_BG) Local $clrSel = _ColorToCOLORREF($COLOR_MENU_SEL) Local $clrText = _ColorToCOLORREF($COLOR_MENU_TEXT) Static $iDrawCount = 0 Static $bFullBarDrawn = False ; Count how many items were drawn in this "draw cycle" $iDrawCount += 1 ; argumentum ; pre-declare all the "Local" in those IF-THEN that could be needed Local $tClient, $iFullWidth, $tFullMenuBar, $hFullBrush Local $tEmptyArea, $hEmptyBrush ; If we are at the first item AND the bar has not yet been drawn If $iPos = 0 And Not $bFullBarDrawn Then ; Get the full window width $tClient = __WinAPI_GetClientRect($hWnd) $iFullWidth = $tClient.right ; Fill the entire menu bar $tFullMenuBar = DllStructCreate($tagRECT) With $tFullMenuBar .left = 0 .top = $top - 1 .right = $iFullWidth + 3 .bottom = $bottom EndWith $hFullBrush = __WinAPI_CreateSolidBrush($clrBG) __WinAPI_FillRect($hDC, $tFullMenuBar, $hFullBrush) __WinAPI_DeleteObject($hFullBrush) EndIf ; After drawing all items, mark as "drawn" If $iDrawCount >= UBound($g_aMenuText) Then $bFullBarDrawn = True $iDrawCount = 0 EndIf ; Draw background for the area AFTER the last menu item If $iPos = (UBound($g_aMenuText) - 1) Then ; Last menu $tClient = __WinAPI_GetClientRect($hWnd) $iFullWidth = $tClient.right ; Fill only the area to the RIGHT of the last menu item If $right < $iFullWidth Then $tEmptyArea = DllStructCreate($tagRECT) With $tEmptyArea .left = $right .top = $top .right = $iFullWidth + __WinAPI_GetSystemMetrics($SM_CXDLGFRAME) .bottom = $bottom EndWith $hEmptyBrush = __WinAPI_CreateSolidBrush($clrBG) __WinAPI_FillRect($hDC, $tEmptyArea, $hEmptyBrush) __WinAPI_DeleteObject($hEmptyBrush) EndIf EndIf ; Draw item background (selected = lighter) Local $bSelected = BitAND($state, $ODS_SELECTED) Local $bHot = BitAND($state, $ODS_HOTLIGHT) Local $hBrush If $bSelected Then $hBrush = __WinAPI_CreateSolidBrush($clrSel) ElseIf $bHot Then $hBrush = __WinAPI_CreateSolidBrush($COLOR_MENU_HOT) Else $hBrush = __WinAPI_CreateSolidBrush($clrBG) EndIf Local $tItemRect = DllStructCreate($tagRECT) With $tItemRect .left = $left .top = $top .right = $right .bottom = $bottom EndWith __WinAPI_FillRect($hDC, $tItemRect, $hBrush) __WinAPI_DeleteObject($hBrush) ; Setup font Local $hFont = __SendMessage($hWnd, $WM_GETFONT, 0, 0) If Not $hFont Then $hFont = __WinAPI_GetStockObject($DEFAULT_GUI_FONT) Local $hOldFont = __WinAPI_SelectObject($hDC, $hFont) __WinAPI_SetBkMode($hDC, $TRANSPARENT) __WinAPI_SetTextColor($hDC, $clrText) ; Draw text Local $tTextRect = DllStructCreate($tagRECT) With $tTextRect .left = $left + 10 .top = $top + 4 .right = $right - 10 .bottom = $bottom - 4 EndWith DllCall($hUser32Dll, "int", "DrawTextW", "handle", $hDC, "wstr", $sText, "int", -1, "ptr", _ DllStructGetPtr($tTextRect), "uint", BitOR($DT_SINGLELINE, $DT_VCENTER, $DT_LEFT)) If $hOldFont Then __WinAPI_SelectObject($hDC, $hOldFont) Return 1 EndFunc ;==>__GUIDarkMode__WM_DRAWITEM Func __GUIDarkMode__WM_MEASUREITEM($hWnd, $iMsg, $iwParam, $ilParam) #forceref $iMsg, $iwParam Local Const $DEFAULT_GUI_FONT = 17 Local $tagMEASUREITEM = "uint CtlType;uint CtlID;uint itemID;uint itemWidth;uint itemHeight;ulong_ptr itemData" Local $t = DllStructCreate($tagMEASUREITEM, $ilParam) If Not IsDllStruct($t) Then Return $GUI_RUNDEFMSG If $t.CtlType <> $ODT_MENU Then Return $GUI_RUNDEFMSG Local $itemID = $t.itemID ; itemID is the control ID, not the position! ; We must derive the position from the itemID Local $iPos = -1 For $i = 0 To UBound($g_aMenuText) - 1 If $itemID = $g_aMenuText[$i][0] Then $iPos = $i ExitLoop EndIf Next ; Fallback: try the itemID directly If $iPos < 0 Then $iPos = $itemID If $iPos < 0 Or $iPos >= UBound($g_aMenuText) Then $iPos = 0 Local $sText = $g_aMenuText[$iPos][1] ; Calculate text dimensions Local $hDC = __WinAPI_GetDC($hWnd) Local $hFont = __SendMessage($hWnd, $WM_GETFONT, 0, 0) If Not $hFont Then $hFont = __WinAPI_GetStockObject($DEFAULT_GUI_FONT) Local $hOldFont = __WinAPI_SelectObject($hDC, $hFont) Local $tSize = __WinAPI_GetTextExtentPoint32($hDC, $sText) Local $iTextWidth = $tSize.X Local $iTextHeight = $tSize.Y __WinAPI_SelectObject($hDC, $hOldFont) __WinAPI_ReleaseDC($hWnd, $hDC) ; Set dimensions with padding (with high DPI) $t.itemWidth = _CalcMenuItemWidth($iDPIpct, $iTextWidth) $t.itemHeight = $iTextHeight + 1 Return 1 EndFunc ;==>__GUIDarkMode__WM_MEASUREITEM Edited Thursday at 11:54 AM by WildByDesign
Kanashius Posted Thursday at 03:20 PM Posted Thursday at 03:20 PM (edited) I had a look at it and worked it out. Now only the Menu items shortly flicker, when they are removed and readded. I think this is an acceptable result. I implemented the Themes differently, so you can now just add themes in the __GUIDarkMenu_StartUp, switching between them with __GUIDarkMenu_SetTheme($hGui, $__GUIDarkMenu_iThemeDark). The UDF with an example at the top: expandcollapse popup#include-once ; #INDEX# ======================================================================================================================= ; Title .........: GUIDarkMenu UDF Library for AutoIt3 ; AutoIt Version : 3.3.18.0 ; Language ......: English ; Description ...: UDF library for applying dark theme to menubar ; Author(s) .....: WildByDesign, Kanashius (including previous code from ahmet, argumentum, UEZ) ; Version .......: 0.9.2 ; =============================================================================================================================== #include <WinAPISysWin.au3> #include <GuiMenu.au3> #include <GUIConstantsEx.au3> #include <APIGdiConstants.au3> #include <WindowsNotifsConstants.au3> #include <WinAPIGdiDC.au3> #include <StructureConstants.au3> #include <AutoItConstants.au3> #include <WinAPIGdi.au3> #include <WinAPIHObj.au3> ; Menu Info Const $ODT_MENU = 1 Const $ODS_SELECTED = 0x0001 Const $ODS_DISABLED = 0x0004 Const $ODS_HOTLIGHT = 0x0040 Global $__GUIDarkMenu_mData[] Global Const $__GUIDarkMenu_iThemeLight = 1, $__GUIDarkMenu_iThemeDark = 2 Global Const $__GUIDarkMenu_vColorBG = "iColorBG", $__GUIDarkMenu_vColor = "iColor", $__GUIDarkMenu_vColorCtrlBG = "iColorCtrlBG", $__GUIDarkMenu_vColorBorder = "iColorBorder" Global Const $__GUIDarkMenu_vColorMenuBG = "iColorMenuBG", $__GUIDarkMenu_vColorMenuHot = "iColorMenuHot", $__GUIDarkMenu_vColorMenuSel = "iColorMenuSel", $__GUIDarkMenu_vColorMenuText = "iColorMenuText" ; ================== EXAMPLE CODE START ================== __GUIDarkMenu_StartUp() Global $hGui = GUICreate("Example", 800, 600) _createMenu($hGui) Local $idButtonReload = GUICtrlCreateButton("Reload menu", 10, 10) Local $idButtonSwitch = GUICtrlCreateButton("Switch menu", 10, 40) GUISetState() While True Switch GUIGetMsg() Case -3 __GUIDarkMenu_Shutdown() Exit Case $idButtonReload _createMenu($hGui) Case $idButtonSwitch _createMenu($hGui, True) EndSwitch WEnd Func _createMenu($hGui, $bSwitchTheme = False) Local $hMenu = _GUICtrlMenu_GetMenu($hGui) While _GUICtrlMenu_GetItemCount($hMenu)>1 _GUICtrlMenu_DeleteMenu($hMenu, 0) WEnd Local $idFile = GUICtrlCreateMenu("File") GUICtrlCreateMenuItem("Test 1", $idFile) GUICtrlCreateMenuItem("Test 2", $idFile) GUICtrlCreateMenu("Edit") GUICtrlCreateMenu("Options") GUICtrlCreateMenu("Help") _GUICtrlMenu_DeleteMenu($hMenu, 0) Local Static $iLastTheme = $__GUIDarkMenu_iThemeDark If $bSwitchTheme Then If $iLastTheme = $__GUIDarkMenu_iThemeLight Then $iLastTheme = $__GUIDarkMenu_iThemeDark Else $iLastTheme = $__GUIDarkMenu_iThemeLight EndIf EndIf __GUIDarkMenu_SetTheme($hGui, $iLastTheme) If @error Then ConsoleWrite("Error settings theme: "&@error&":"&@extended&@crlf) EndFunc ; ================== EXAMPLE CODE END ================== Func __GUIDarkMenu_StartUp() $__GUIDarkMenu_mData.hDllGDI = DllOpen("gdi32.dll") $__GUIDarkMenu_mData.hDllUser = DllOpen("user32.dll") Local $mGuis[] $__GUIDarkMenu_mData.mGuis = $mGuis $__GUIDarkMenu_mData.hProc = DllCallbackRegister('__GUIDarkMenu_WinProc', 'ptr', 'hwnd;uint;wparam;lparam') Local $mThemes[] Local $mDarkTheme[] $mDarkTheme[$__GUIDarkMenu_vColorBG] = 0x121212 $mDarkTheme[$__GUIDarkMenu_vColor] = 0xE0E0E0 $mDarkTheme[$__GUIDarkMenu_vColorCtrlBG] = 0x202020 $mDarkTheme[$__GUIDarkMenu_vColorBorder] = 0x3F3F3F $mDarkTheme[$__GUIDarkMenu_vColorMenuBG] = _WinAPI_ColorAdjustLuma($mDarkTheme[$__GUIDarkMenu_vColorBG], 5) $mDarkTheme[$__GUIDarkMenu_vColorMenuHot] = _WinAPI_ColorAdjustLuma($mDarkTheme[$__GUIDarkMenu_vColorMenuBG], 20) $mDarkTheme[$__GUIDarkMenu_vColorMenuSel] = _WinAPI_ColorAdjustLuma($mDarkTheme[$__GUIDarkMenu_vColorMenuBG], 10) $mDarkTheme[$__GUIDarkMenu_vColorMenuText] = $mDarkTheme[$__GUIDarkMenu_vColor] $mThemes[$__GUIDarkMenu_iThemeDark] = $mDarkTheme Local $mLightTheme[] $mLightTheme[$__GUIDarkMenu_vColorBG] = 0xFFFFFF $mLightTheme[$__GUIDarkMenu_vColor] = 0x000000 $mLightTheme[$__GUIDarkMenu_vColorCtrlBG] = 0xDDDDDD $mLightTheme[$__GUIDarkMenu_vColorBorder] = 0xCCCCCC $mLightTheme[$__GUIDarkMenu_vColorMenuBG] = _WinAPI_ColorAdjustLuma($mLightTheme[$__GUIDarkMenu_vColorBG], 95) $mLightTheme[$__GUIDarkMenu_vColorMenuHot] = _WinAPI_ColorAdjustLuma($mLightTheme[$__GUIDarkMenu_vColorMenuBG], 80) $mLightTheme[$__GUIDarkMenu_vColorMenuSel] = _WinAPI_ColorAdjustLuma($mLightTheme[$__GUIDarkMenu_vColorMenuBG], 70) $mLightTheme[$__GUIDarkMenu_vColorMenuText] = $mLightTheme[$__GUIDarkMenu_vColor] $mThemes[$__GUIDarkMenu_iThemeLight] = $mLightTheme $__GUIDarkMenu_mData.mThemes = $mThemes EndFunc Func __GUIDarkMenu_Shutdown() If Not __GUIDarkMenu__IsInitialized() Then Return SetError(2, 0, False) For $hGui In MapKeys($__GUIDarkMenu_mData.mGuis) __GUIDarkMenu_GuiRemove($hGui) Next DllCallbackFree($__GUIDarkMenu_mData.hProc) DllClose($__GUIDarkMenu_mData.hDllGDI) DllClose($__GUIDarkMenu_mData.hDllUser) Local $mNewMap[] $__GUIDarkMenu_mData = $mNewMap Return True EndFunc Func __GUIDarkMenu_GuiAdd($hGui) If Not __GUIDarkMenu__IsInitialized() Then Return SetError(2, 0, False) If MapExists($__GUIDarkMenu_mData.mGuis, $hGui) Then Return True Local $mGui[] Local $iDpiPct = Round(__WinAPI_GetDpiForWindow($hGUI) / 96, 2) * 100 $mGui.iDpi = @error?100:$iDpiPct Local $hProc = _WinAPI_SetWindowLong($hGui, -4, DllCallbackGetPtr($__GUIDarkMenu_mData.hProc)) If @error Then Return SetError(2, 0, False) $mGui.hPrevProc = $hProc $mGui.iTheme = $__GUIDarkMenu_iThemeLight $mGui.iTextSpaceHori = 20 $mGui.iTextSpaceVert = 8 $mGui.iFontSize = 9 $__GUIDarkMenu_mData["mGuis"][$hGui] = $mGui Return True EndFunc Func __GUIDarkMenu_GuiRemove($hGui) If Not __GUIDarkMenu__IsInitialized() Then Return SetError(2, 0, False) If Not MapExists($__GUIDarkMenu_mData.mGuis, $hGui) Then Return SetError(1, 1, False) _WinAPI_SetWindowLong($hGui, -4, $__GUIDarkMenu_mData.mGuis[$hGui].hPrevProc) MapRemove($__GUIDarkMenu_mData.mGuis, $hGui) Return True EndFunc Func __GUIDarkMenu__IsInitialized() Return UBound($__GUIDarkMenu_mData)>0 EndFunc Func __GUIDarkMenu_WinProc($hWnd, $iMsg, $iwParam, $ilParam) Local $sContinue = $GUI_RUNDEFMSG Switch $iMsg Case $WM_WINDOWPOSCHANGED $sContinue = __GUIDarkMode__WM_WINDOWPOSCHANGED($hWnd, $iMsg, $iwParam, $ilParam) Case $WM_ACTIVATE $sContinue = __GUIDarkMode__WM_ACTIVATE($hWnd, $iMsg, $iwParam, $ilParam) Case $WM_MEASUREITEM $sContinue = __GUIDarkMode__WM_MEASUREITEM($hWnd, $iMsg, $iwParam, $ilParam) Case $WM_DRAWITEM $sContinue = __GUIDarkMode__WM_DRAWITEM($hWnd, $iMsg, $iwParam, $ilParam) EndSwitch If $sContinue=$GUI_RUNDEFMSG And MapExists($__GUIDarkMenu_mData.mGuis, $hWnd) Then Return _WinAPI_CallWindowProc($__GUIDarkMenu_mData.mGuis[$hWnd].hPrevProc, $hWnd, $iMsg, $iwParam, $ilParam) EndFunc Func __GUIDarkMenu_SetTheme($hGui, $iTheme) If Not __GUIDarkMenu__IsInitialized() Then Return SetError(2, 0, False) __GUIDarkMenu_GuiAdd($hGui) If @error Then Return SetError(1, 1, False) If Not MapExists($__GUIDarkMenu_mData.mThemes, $iTheme) Then Return SetError(1, 2, False) $__GUIDarkMenu_mData["mGuis"][$hGui]["iTheme"] = $iTheme Local $hMenu = _GUICtrlMenu_GetMenu($hGui) If Not $hMenu Then Return False For $i = 0 To _GUICtrlMenu_GetItemCount($hMenu) - 1 _GUICtrlMenu_SetItemType($hMenu, $i, $MFT_OWNERDRAW, True) Next __GUIDarkMenu__MenuBarSetBKColor($hMenu, __GUIDarkMenu__GetColor($hGui, $__GUIDarkMenu_vColorMenuBG)) _GUICtrlMenu_DrawMenuBar($hGui) _WinAPI_RedrawWindow($hGui, 0, 0, BitOR($RDW_INVALIDATE, $RDW_UPDATENOW)) Return True EndFunc Func __GUIDarkMenu__GetColor($hGui, $sColor) If Not __GUIDarkMenu__IsInitialized() Then Return SetError(2, 0, False) If Not MapExists($__GUIDarkMenu_mData.mGuis, $hGui) Then Return SetError(1, 1, 0) If Not MapExists($__GUIDarkMenu_mData.mThemes[$__GUIDarkMenu_mData.mGuis[$hGui].iTheme], $sColor) Then Return SetError(1, 2, 0) Return $__GUIDarkMenu_mData.mThemes[$__GUIDarkMenu_mData.mGuis[$hGui].iTheme][$sColor] EndFunc Func __GUIDarkMenu__MenuBarSetBKColor($hMenu, $iColor) Local $tInfo,$aResult Local $hBrush = DllCall($__GUIDarkMenu_mData.hDllGDI, 'hwnd', 'CreateSolidBrush', 'int', $iColor) If @error Then Return ;$tInfo = DllStructCreate("int Size;int Mask;int Style;int YMax;int hBack;int ContextHelpID;ptr MenuData") $tInfo = DllStructCreate("int Size;int Mask;int Style;int YMax;handle hBack;int ContextHelpID;ptr MenuData") DllStructSetData($tInfo, "Mask", 2) DllStructSetData($tInfo, "hBack", $hBrush[0]) DllStructSetData($tInfo, "Size", DllStructGetSize($tInfo)) $aResult = DllCall($__GUIDarkMenu_mData.hDllUser, "int", "SetMenuInfo", "hwnd", $hMenu, "ptr", DllStructGetPtr($tInfo)) Return $aResult[0] <> 0 EndFunc ;==>_GUICtrlMenu_SetMenuBackground Func __WinAPI_GetDpiForWindow($hWnd) Local $aResult = DllCall($__GUIDarkMenu_mData.hDllUser, "uint", "GetDpiForWindow", "hwnd", $hWnd) ;requires Win10 v1607+ / no server support If Not IsArray($aResult) Or @error Then Return SetError(1, @extended, 0) If Not $aResult[0] Then Return SetError(2, @extended, 0) Return $aResult[0] EndFunc ;==>__WinAPI_GetDpiForWindow Func __GUIDarkMenu__GetTextDimension($hWnd, $sText) ; Calculate text dimensions Local $hDC = _WinAPI_GetDC($hWnd) Local $hFont = _SendMessage($hWnd, $WM_GETFONT, 0, 0) If Not $hFont Then $hFont = _WinAPI_GetStockObject($DEFAULT_GUI_FONT) Local $hOldFont = _WinAPI_SelectObject($hDC, $hFont) Local $tSize = _WinAPI_GetTextExtentPoint32($hDC, $sText) Local $iTextWidth = $tSize.X + 6 Local $iTextHeight = $tSize.Y _WinAPI_SelectObject($hDC, $hOldFont) _WinAPI_ReleaseDC($hWnd, $hDC) Local $arSize = [$iTextWidth, $iTextHeight] Return $arSize EndFunc Func __GUIDarkMode__WM_MEASUREITEM($hWnd, $iMsg, $wParam, $lParam) #forceref $iMsg, $wParam If Not MapExists($__GUIDarkMenu_mData.mGuis, $hWnd) Then Return $GUI_RUNDEFMSG Local $tagMEASUREITEM = "uint CtlType;uint CtlID;uint itemID;uint itemWidth;uint itemHeight;ulong_ptr itemData" Local $t = DllStructCreate($tagMEASUREITEM, $lParam) If Not IsDllStruct($t) Then Return $GUI_RUNDEFMSG If $t.CtlType <> $ODT_MENU Then Return $GUI_RUNDEFMSG Local $itemID = $t.itemID Local $sText = _GUICtrlMenu_GetItemText(_GUICtrlMenu_GetMenu($hWnd), $itemID, False) Local $arSize = __GUIDarkMenu__GetTextDimension($hWnd, $sText) ; Set dimensions with padding (with high DPI) $t.itemWidth = $arSize[0] + $__GUIDarkMenu_mData.mGuis[$hWnd].iTextSpaceHori/2 $t.itemHeight = $arSize[1] + $__GUIDarkMenu_mData.mGuis[$hWnd].iTextSpaceVert Return $GUI_RUNDEFMSG EndFunc ;==>WM_MEASUREITEM_Handler Func __GUIDarkMode__WM_DRAWITEM($hWnd, $iMsg, $wParam, $lParam) #forceref $iMsg, $wParam Local $tagDRAWITEM = "uint CtlType;uint CtlID;uint itemID;uint itemAction;uint itemState;ptr hwndItem;handle hDC;" & _ "long left;long top;long right;long bottom;ulong_ptr itemData" Local $tDrawItem = DllStructCreate($tagDRAWITEM, $lParam) If Not IsDllStruct($tDrawItem) Then Return $GUI_RUNDEFMSG If $tDrawItem.CtlType <> $ODT_MENU Then Return $GUI_RUNDEFMSG Local $hDC = $tDrawItem.hDC Local $iLeft = $tDrawItem.left Local $iTop = $tDrawItem.top Local $iRight = $tDrawItem.right Local $iBottom = $tDrawItem.bottom Local $iState = $tDrawItem.itemState Local $iItemID = $tDrawItem.itemID Local $hMenu = _GUICtrlMenu_GetMenu($hWnd) ; convert itemID to position Local $iPos = -1 For $i = 0 To _GUICtrlMenu_GetItemCount($hMenu) - 1 If $iItemID = _GUICtrlMenu_GetItemID($hMenu, $i) Then $iPos = $i ExitLoop EndIf Next If $iPos < 0 Then Return $GUI_RUNDEFMSG ; something must have gone seriously wrong Local $sText = _GUICtrlMenu_GetItemText($hMenu, $iPos) $sText = StringReplace($sText, "&", "") ; Draw item background (selected = lighter) Local $bSelected = BitAND($iState, $ODS_SELECTED) Local $bHot = BitAND($iState, $ODS_HOTLIGHT) Local $hBrush ; __GUIDarkMenu__ColorRGBToBGR() If $bSelected Then $hBrush = _WinAPI_CreateSolidBrush(__GUIDarkMenu__GetColor($hGui, $__GUIDarkMenu_vColorMenuSel)) ElseIf $bHot Then $hBrush = _WinAPI_CreateSolidBrush(__GUIDarkMenu__GetColor($hGui, $__GUIDarkMenu_vColorMenuHot)) Else $hBrush = _WinAPI_CreateSolidBrush(__GUIDarkMenu__GetColor($hGui, $__GUIDarkMenu_vColorMenuBG)) EndIf Local $tItemRect = DllStructCreate($tagRECT) With $tItemRect .left = $iLeft .top = $iTop .right = $iRight .bottom = $iBottom EndWith _WinAPI_FillRect($hDC, $tItemRect, $hBrush) _WinAPI_DeleteObject($hBrush) ; Setup font Local $hFont = __GUIDarkMenu__CreateMenuFontByName("Segoe UI", $__GUIDarkMenu_mData.mGuis[$hWnd].iFontSize) If Not $hFont Then $hFont = _WinAPI_GetStockObject($DEFAULT_GUI_FONT) Local $hOldFont = _WinAPI_SelectObject($hDC, $hFont) _WinAPI_SetBkMode($hDC, $TRANSPARENT) _WinAPI_SetTextColor($hDC, __GUIDarkMenu__GetColor($hGui, $__GUIDarkMenu_vColorMenuText)) ; Draw text Local $tTextRect = DllStructCreate($tagRECT) With $tTextRect .left = $iLeft + $__GUIDarkMenu_mData.mGuis[$hGui].iTextSpaceHori/2 .top = $iTop + $__GUIDarkMenu_mData.mGuis[$hGui].iTextSpaceVert/2 .right = $iRight - $__GUIDarkMenu_mData.mGuis[$hGui].iTextSpaceHori/2 .bottom = $iBottom - $__GUIDarkMenu_mData.mGuis[$hGui].iTextSpaceVert/2 EndWith DllCall($__GUIDarkMenu_mData.hDllUser, "int", "DrawTextW", "handle", $hDC, "wstr", $sText, "int", -1, "ptr", _ DllStructGetPtr($tTextRect), "uint", BitOR($DT_SINGLELINE, $DT_VCENTER, $DT_LEFT)) If $hOldFont Then _WinAPI_SelectObject($hDC, $hOldFont) Return $GUI_RUNDEFMSG EndFunc ;==>WM_DRAWITEM Func __GUIDarkMode__WM_WINDOWPOSCHANGED($hWnd, $iMsg, $wParam, $lParam) #forceref $iMsg, $wParam, $lParam If $hWnd <> $hGUI Then Return $GUI_RUNDEFMSG __GUIDarkMode__DrawUAHMenuNCBottomLine($hWnd) Return $GUI_RUNDEFMSG EndFunc ;==>WM_WINDOWPOSCHANGED_Handler Func __GUIDarkMode__DrawUAHMenuNCBottomLine($hWnd) Local $rcClient = _WinAPI_GetClientRect($hWnd) DllCall($__GUIDarkMenu_mData.hDllUser, "int", "MapWindowPoints", _ "hwnd", $hWnd, _ ; hWndFrom "hwnd", 0, _ ; hWndTo "ptr", DllStructGetPtr($rcClient), _ "uint", 2) ;number of points - 2 for RECT structure Local $rcWindow = _WinAPI_GetWindowRect($hWnd) _WinAPI_OffsetRect($rcClient, -$rcWindow.left, -$rcWindow.top) Local $rcAnnoyingLine = DllStructCreate($tagRECT) $rcAnnoyingLine.left = $rcClient.left $rcAnnoyingLine.top = $rcClient.top $rcAnnoyingLine.right = $rcClient.right $rcAnnoyingLine.bottom = $rcClient.bottom $rcAnnoyingLine.bottom = $rcAnnoyingLine.top $rcAnnoyingLine.top = $rcAnnoyingLine.top - 1 Local $hRgn = _WinAPI_CreateRectRgn(0,0,8000,8000) Local $hDC = _WinAPI_GetDCEx($hWnd,$hRgn, BitOR($DCX_WINDOW,$DCX_INTERSECTRGN)) Local $hFullBrush = _WinAPI_CreateSolidBrush(__GUIDarkMenu__GetColor($hWnd, $__GUIDarkMenu_vColorMenuBG)) _WinAPI_FillRect($hDC, $rcAnnoyingLine, $hFullBrush) _WinAPI_ReleaseDC($hWnd, $hDC) _WinAPI_DeleteObject($hFullBrush) EndFunc ;==>__GUIDarkMode__DrawUAHMenuNCBottomLine Func __GUIDarkMode__WM_ACTIVATE($hWnd, $MsgID, $wParam, $lParam) #forceref $MsgID, $wParam, $lParam If $hWnd <> $hGUI Then Return $GUI_RUNDEFMSG __GUIDarkMode__DrawUAHMenuNCBottomLine($hWnd) Return $GUI_RUNDEFMSG EndFunc ;==>WM_ACTIVATE_Handler Func __GUIDarkMenu__ColorRGBToBGR($iColor) ;RGB to BGR Local $iR = BitAND(BitShift($iColor, 16), 0xFF) Local $iG = BitAND(BitShift($iColor, 8), 0xFF) Local $iB = BitAND($iColor, 0xFF) Return BitOR(BitShift($iB, -16), BitShift($iG, -8), $iR) EndFunc ;==>__GUIDarkMenu__ColorRGBToBGR Func __GUIDarkMenu__CreateFont($nHeight, $nWidth, $nEscape, $nOrientn, $fnWeight, $bItalic, $bUnderline, $bStrikeout, $nCharset, $nOutputPrec, $nClipPrec, $nQuality, $nPitch, $ptrFontName) Local $hFont = DllCall($__GUIDarkMenu_mData.hDllGDI , "hwnd", "CreateFont", _ "int", $nHeight, _ "int", $nWidth, _ "int", $nEscape, _ "int", $nOrientn, _ "int", $fnWeight, _ "long", $bItalic, _ "long", $bUnderline, _ "long", $bStrikeout, _ "long", $nCharset, _ "long", $nOutputPrec, _ "long", $nClipPrec, _ "long", $nQuality, _ "long", $nPitch, _ "ptr", $ptrFontName) Return $hFont[0] EndFunc Func __GUIDarkMenu__CreateMenuFontByName($sFontName, $nHeight = 9, $nWidth = 400) Local $stFontName = DllStructCreate("char[260]") DllStructSetData($stFontName, 1, $sFontName) Local $hDC = _WinAPI_GetDC(0) Local $nPixel = _WinAPI_GetDeviceCaps($hDC, 90) $nHeight = 0 - _WinAPI_MulDiv($nHeight, $nPixel, 72) _WinAPI_ReleaseDC(0, $hDC) Local $hFont = __GUIDarkMenu__CreateFont($nHeight, 0, 0, 0, $nWidth, 0, 0, 0, 0, 0, 0, 0, 0, DllStructGetPtr($stFontName)) $stFontName = 0 Return $hFont EndFunc But this is currently only for the MenuBar at the top, not the submenus Edited Thursday at 03:27 PM by Kanashius WildByDesign 1 My Website: Kanashius Webside (Some of my Programs you can find there)
WildByDesign Posted Thursday at 05:50 PM Author Posted Thursday at 05:50 PM 2 hours ago, Kanashius said: I had a look at it and worked it out. Now only the Menu items shortly flicker, when they are removed and readded. I think this is an acceptable result. I implemented the Themes differently, so you can now just add themes in the __GUIDarkMenu_StartUp, switching between them with __GUIDarkMenu_SetTheme($hGui, $__GUIDarkMenu_iThemeDark). Patrick, you are brilliant! The more code changes that I see from you, the more I understand how intelligent your techniques and methods are to the way that you think and plan. I am incredibly impressed and I feel like I'm going to learn a lot just from reading over your code changes. Even the way that you are organizing the DLL loading is awesome. 2 hours ago, Kanashius said: But this is currently only for the MenuBar at the top, not the submenus I personally don't think that we need to worry about subclassing and coloring the actual menus. Those are just done by the system as win32-darkmode menus. But if you want to get super colorful, that is up to you. But I really don't think we need it. Anyway, I tested your latest GUIDarkMenu and it works wonderfully. I would suggest that you commit those changes in the repo because it is working much better now. Thank you so much for these improvements. I find it incredible how we already do a few things with Files Au3 that even File Explorer does not do. Plus we do many of those things faster. Kanashius 1
Kanashius Posted Thursday at 09:17 PM Posted Thursday at 09:17 PM Thank you, I am glad you like the changes I pushed a commit, adapting to the new code. 3 hours ago, WildByDesign said: Those are just done by the system as win32-darkmode menus. Ah, ok, I thought that was part of the old ModernMenu UDF. Yeah, I really like it and it is coming together. WildByDesign 1 My Website: Kanashius Webside (Some of my Programs you can find there)
WildByDesign Posted Thursday at 09:22 PM Author Posted Thursday at 09:22 PM 1 minute ago, Kanashius said: Ah, ok, I thought that was part of the old ModernMenu UDF. You are right, the old ModernMenuRaw UDF did have the capability to color the actual menus. But that was at a time when Windows did not have native dark mode for menus. So you could technically add it if you wanted to. But it adds more complexity and isn’t really needed now.
WildByDesign Posted Friday at 01:45 PM Author Posted Friday at 01:45 PM (edited) 16 hours ago, Kanashius said: I pushed a commit, adapting to the new code. I was just testing this right now. All of the language switching seems to work nicely with the updated menu code. I only have one concern at the moment. The menu text measurement seems to be leaving quite a bit of space on the right side of each menu. For example, see screenshot: I had the menu text measurement fixed previously but it seems to have regressed. But if it shows measured properly on your system, it could potentially be DPI-related and we may need to make adjustments. Edited Friday at 01:46 PM by WildByDesign
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now