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 March 5 Posted March 5 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 March 5 Author Posted March 5 (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 March 5 by WildByDesign
Kanashius Posted March 5 Posted March 5 (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 March 5 by Kanashius WildByDesign 1 My Website: Kanashius Webside (Some of my Programs you can find there)
WildByDesign Posted March 5 Author Posted March 5 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 March 5 Posted March 5 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 March 5 Author Posted March 5 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 March 6 Author Posted March 6 (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 March 6 by WildByDesign
WildByDesign Posted March 18 Author Posted March 18 @Kanashius There are a few minor changes that I would like to make but I am not certain if it would cause any problems for the work that you are doing behind-the-scenes. If I put together a couple of smaller PRs and assuming they get accepted/merged, would that cause problems for your current work on it? SOLVE-SMART and Kanashius 2
SOLVE-SMART Posted March 19 Posted March 19 Hi folks, maybe it's a bit over the top, but we could use Discussions directly on GitHub or even a roadmap feature (on GitHub) to express the thoughts and upcoming features (plans). Even the way with "GitHub issues" would be very common, but I also see the downside of missing clever people who usually help here and not on GitHub because they are not active their or in the GitHub Organization "AutoIt Community". Pro: it's all in one place, well organized (via Kanban or Roadmap board) and the overview about "whats next", who is working on what is good. Cons: this would decrease the activity here and we would lose people to support the project. --------- .. few thoughts about this. I guess I mix up the idea of an team (which is working on a concrete project) versus a community (that can have several contributors, but who are not always active or have only been active once). Best regards Sven WildByDesign 1 ==> AutoIt related: 🔗 Organization AutoIt Community, 🔗 GitHub, 🔗 Discord Server, 🔗 Cheat Sheet, 🔗 autoit-webdriver-boilerplate Spoiler 🌍 Au3Forums 🎲 AutoIt (en) Cheat Sheet 📊 AutoIt limits/defaults 💎 Code Katas: [...] (comming soon) 🎭 Collection of GitHub users with AutoIt projects 🐞 False-Positives 🔮 Me on GitHub 💬 Opinion about new forum sub category 📑 UDF wiki list ✂ VSCode-AutoItSnippets 📑 WebDriver FAQs 👨🏫 WebDriver Tutorial (coming soon)
Kanashius Posted March 19 Posted March 19 12 hours ago, WildByDesign said: If I put together a couple of smaller PRs and assuming they get accepted/merged, would that cause problems for your current work on it? I go through everything, so there will not really be something I do not touch, but do not let that stop you from further progress. I will look over all changes and transfer them, that is no problem. I'm happy for any new features. I think I'm progressing pretty well, so there should be something soonish as well SOLVE-SMART and WildByDesign 1 1 My Website: Kanashius Webside (Some of my Programs you can find there)
WildByDesign Posted March 19 Author Posted March 19 6 hours ago, SOLVE-SMART said: but we could use Discussions directly on GitHub or even a roadmap feature (on GitHub) to express the thoughts and upcoming features (plans). Even the way with "GitHub issues" would be very common That is a very good point. You will have to enable Discussions though because it is disabled by default on any repos. But yes, from either Issues or Discussions we can discuss planning and all things like that. I agree. 7 minutes ago, Kanashius said: I go through everything, so there will not really be something I do not touch, but do not let that stop you from further progress. I will look over all changes and transfer them, that is no problem. I'm happy for any new features. That sounds great. I will try to keep my PRs relatively small so that it is easier to port over to your upcoming refactoring changes. And I'll do a separate PR for each feature-related change so that it should be easier for you as well. 9 minutes ago, Kanashius said: I think I'm progressing pretty well, so there should be something soonish as well I'm excited to when you are done. Whenever you go silent for days/weeks, you always show up with Christmas-like surprises. So I always know to be patient and have no worries. I have a few code changes ready to implement and a bunch more ideas after that. So this project is going to be incredible. SOLVE-SMART and Kanashius 1 1
WildByDesign Posted March 29 Author Posted March 29 I just added a pull request for MsgBox Improvements to allow for dark mode MsgBox. It switches properly between light mode and dark mode. @Kanashius I noticed a problem with our About dialog (not related to the new MsgBox improvements PR). The problem is that obtaining the version number of the TreeListExplorer UDF fails when the binaries are compiled. It makes sense that it would fail because we don't have the script .au3 files to parse once compiled as binary. Do you think that we should just manually add the TreeListExplorer UDF version to the About dialog?
argumentum Posted March 29 Posted March 29 (edited) 1 hour ago, WildByDesign said: The problem is that obtaining the version number ... Global Const $__g_SuperStuff_sVersion = "0.0.0.1" ... Func _SuperStuff_Version() Return $__g_SuperStuff_sVersion EndFunc ;==>_SuperStuff_Version ... Edited March 29 by argumentum WildByDesign 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
Kanashius Posted March 29 Posted March 29 6 hours ago, WildByDesign said: Do you think that we should just manually add the TreeListExplorer UDF version to the About dialog? The code was just to extract from sources. There are ways to get the source code from a compiled file: But those require including the sourcecode in the binary/... so I'm not sure its worth it. I thought of it more as a way to write a script, which is executed before the compilation of the release to write all versions of the used udfs into a Versions.au3, which is then included in the compiled script. This way it would not have to be done manually and would automatically be done => not missed when bumping the version or something... But it was planned as more of a convenience thing in an extra tool WildByDesign 1 My Website: Kanashius Webside (Some of my Programs you can find there)
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