There doesn't seem to be an easy workaround, and the menu items should be drawn using the owner-drawn method.
Tested only on Windows 10. Testing may be needed for the classic theme in Windows 7.
Code: Select all
EnableExplicit
UsePNGImageDecoder()
Import "UxTheme.lib"
DrawThemeTextEx.l(hTheme, hdc, iPartId.l, iStateId.l, *pszText, cchText.l, dwTextFlags.l, *pRect, *pOptions.DTTOPTS)
EndImport
; API Constants.
#TMT_MENUFONT = 803
#MENU_POPUPBACKGROUND = 9
#MENU_POPUPITEM = 14
#MPI_NORMAL = 1
#MPI_HOT = 2
;- User Data.
Structure SubMenuItem
hIcon.i
IconW.w
IconH.w
Text.s
EndStructure
#SubmenuItemMargin = 2
Global NewList SubMenuItems.SubMenuItem()
Global g_MenuBgColorNormal.l
Global g_MenuBgColorHot.l
; If the DPI of the window containing the menu changes, it must be retrieved again. (When future PB supports PerMonitorV2 DPI awareness)
Global g_hMenuFont
Global g_MenuItemHeight.l
Procedure.l GetMenuDefaultBgColor()
Protected Image, hDC, hThemeMenu, Color.l, rt.RECT
Image = CreateImage(#PB_Any, 32, 32)
If Image
hDC = StartDrawing(ImageOutput(Image))
If hDC
hThemeMenu = OpenThemeData_(0, "Menu")
If hThemeMenu
rt\right = 32
rt\bottom = 32
DrawThemeBackground_(hThemeMenu, hDC, #MENU_POPUPBACKGROUND, 0, @rt, 0)
Color = Point(16, 16)
CloseThemeData_(hThemeMenu)
EndIf
StopDrawing()
EndIf
FreeImage(Image)
EndIf
ProcedureReturn Color
EndProcedure
Procedure.l GetMenuBgColor(State)
Protected Image, hDC, hThemeMenu, Color.l, rt.RECT
Image = CreateImage(#PB_Any, 32, 32, 24, GetMenuDefaultBgColor())
If Image
hDC = StartDrawing(ImageOutput(Image))
If hDC
hThemeMenu = OpenThemeData_(0, "Menu")
If hThemeMenu
rt\right = 32
rt\bottom = 32
DrawThemeBackground_(hThemeMenu, hDC, #MENU_POPUPITEM, State, @rt, 0)
Color = Point(16, 16)
CloseThemeData_(hThemeMenu)
EndIf
StopDrawing()
EndIf
FreeImage(Image)
EndIf
ProcedureReturn Color
EndProcedure
Procedure LoadMenuFont()
Protected lf.LOGFONT, hFont, hTheme
If IsAppThemed_() ;And IsThemeActive_()
hTheme = OpenThemeData_(0, "Menu")
;Debug "App Themed"
EndIf
If GetThemeSysFont_(hTheme, #TMT_MENUFONT, @lf) = #S_OK
hFont = CreateFontIndirect_(lf)
EndIf
If hTheme : CloseThemeData_(hTheme) : EndIf
ProcedureReturn hFont
EndProcedure
Procedure WndProc_MenuItemWindow(hWnd, uMsg, wParam, lParam)
Protected Result = #PB_ProcessPureBasicEvents
If uMsg = #WM_ENTERMENULOOP And wParam
;PostMessage_(hWnd, #WM_CANCELMODE, 0, 0)
EndMenu_()
ProcedureReturn 0
EndIf
ProcedureReturn Result
EndProcedure
; I don't know the formula for calculating the exact height of menu items based on different DPI settings. :(
; Changes may be required to support PerMonitorV2 DPI awareness. (for future PB versions)
Procedure.l GetMenuItemHeight(ParentWindow)
Protected rt.RECT, Menu
Protected Win = OpenWindow(#PB_Any, 0, 0, 0, 0, "", #PB_Window_BorderLess | #PB_Window_Invisible | #PB_Window_NoGadgets | #PB_Window_NoActivate | #PB_Window_WindowCentered, WindowID(ParentWindow))
If Win
SetWindowCallback(@WndProc_MenuItemWindow(), Win)
Menu = CreatePopupImageMenu(#PB_Any)
If Menu
MenuItem(0, "Dummy")
Protected pt.POINT
MapWindowPoints_(WindowID(Win), 0, @pt, 1)
DisplayPopupMenu(Menu, WindowID(Win), pt\x, pt\y)
GetMenuItemRect_(WindowID(Win), MenuID(Menu), 0, @rt)
FreeMenu(Menu)
EndIf
CloseWindow(Win)
EndIf
ProcedureReturn rt\bottom - rt\top
EndProcedure
Procedure WndProc_MainWindow(hWnd, uMsg, wParam, lParam)
Protected Result = #PB_ProcessPureBasicEvents
If uMsg = #WM_MEASUREITEM
If wParam = 0 And lParam
Protected *mi.MEASUREITEMSTRUCT = lParam
Protected *iteminfo.SubMenuItem
If *mi\CtlType = #ODT_MENU And *mi\itemData
*iteminfo = *mi\itemData
Protected hdc = GetDC_(hWnd)
If hdc
Protected size.SIZE
Protected hFontOld = SelectObject_(hdc, g_hMenuFont)
GetTextExtentPoint32_(hdc, *iteminfo\Text, Len(*iteminfo\Text), @size)
*mi\itemWidth = size\cx + *iteminfo\IconW + DesktopScaledX(16)
*mi\itemHeight = g_MenuItemHeight
SelectObject_(hdc, hFontOld)
ReleaseDC_(hWnd, hdc)
ProcedureReturn #True
EndIf
EndIf
EndIf
EndIf
If uMsg = #WM_DRAWITEM
If wParam = 0 And lParam
Protected *di.DRAWITEMSTRUCT = lParam
Protected State
If *di\CtlType = #ODT_MENU
*iteminfo = *di\itemData
; If *di\itemState & #ODS_NOACCEL
; Debug "ODS_NOACCEL"
; EndIf
; If *di\itemState & #ODS_DEFAULT
; Debug "ODS_DEFAULT"
; EndIf
; If *di\itemState & #ODS_CHECKED
; Debug "ODS_CHECKED"
; EndIf
; If *di\itemState & #ODS_DISABLED
; Debug "ODS_DISABLED"
; EndIf
; If *di\itemState & #ODS_GRAYED
; Debug "ODS_GRAYED"
; EndIf
; If *di\itemState & #ODS_HOTLIGHT
; Debug "ODS_HOTLIGHT"
; EndIf
If *di\itemState & #ODS_SELECTED
;SetBkMode_(*di\hDC, #TRANSPARENT)
State = #MPI_HOT
Else
State = #MPI_NORMAL
EndIf
Protected hThemeMenu = OpenThemeData_(0, "Menu")
If hThemeMenu
If State = #MPI_NORMAL
DrawThemeBackground_(hThemeMenu, *di\hDC, #MENU_POPUPBACKGROUND, 0, @*di\rcItem, 0)
Else
DrawThemeBackground_(hThemeMenu, *di\hDC, #MENU_POPUPITEM, State, @*di\rcItem, 0)
EndIf
Protected rt.RECT
CopyStructure(*di\rcItem, rt, RECT)
rt\left + *iteminfo\IconW + DesktopScaledX(16)
rt\right = *di\rcItem\right
DrawThemeTextEx(hThemeMenu, *di\hDC, #MENU_POPUPITEM, State, @*iteminfo\Text, Len(*iteminfo\Text), #DT_LEFT | #DT_VCENTER | #DT_SINGLELINE | #DT_EXPANDTABS, rt, 0)
CloseThemeData_(hThemeMenu)
EndIf
If *di\itemState & #ODS_SELECTED
Protected img = CreateImage(#PB_Any, *iteminfo\IconW, *iteminfo\IconH, 24, g_MenuBgColorHot)
Else
img = CreateImage(#PB_Any, *iteminfo\IconW, *iteminfo\IconH, 24, g_MenuBgColorNormal)
EndIf
If img
hdc = StartDrawing(ImageOutput(img))
If hdc
DrawAlphaImage(*iteminfo\hIcon, 0, 0)
StretchBlt_(*di\hDC, *di\rcItem\left + DesktopScaledX(2), *di\rcItem\top + ((*di\rcItem\bottom - *di\rcItem\top) - DesktopScaledY(*iteminfo\IconH)) / 2, DesktopScaledX(*iteminfo\IconW), DesktopScaledY(*iteminfo\IconH), hdc, 0, 0, *iteminfo\IconW, *iteminfo\IconH, #SRCCOPY)
StopDrawing()
EndIf
FreeImage(img)
EndIf
ProcedureReturn #True
EndIf
EndIf
EndIf
ProcedureReturn Result
EndProcedure
If OpenWindow(0, 600, 250, 200, 100, "Menu Example")
Define hImg = LoadImage(0, #PB_Compiler_Home + "examples/sources/Data/ToolBar/Open.png")
g_hMenuFont = LoadMenuFont()
;Debug "g_hMenuFont " + g_hMenuFont
g_MenuItemHeight = GetMenuItemHeight(0)
;Debug "g_MenuItemHeight " + g_MenuItemHeight
g_MenuBgColorNormal.l = GetMenuBgColor(#MPI_NORMAL)
g_MenuBgColorHot.l = GetMenuBgColor(#MPI_HOT)
SetWindowCallback(@WndProc_MainWindow(), 0)
Define menu_app = CreateImageMenu(#PB_Any, WindowID(0))
If menu_app
MenuTitle("title1")
OpenSubMenu("123")
MenuItem(1, "123")
CloseSubMenu()
MenuItem(10, "10" +Chr(9)+"Ctrl+O")
MenuItem(11, "11" +Chr(9)+"Ctrl+S")
MenuItem(12, "12"+Chr(9)+"Ctrl+A")
MenuItem(13, "13" +Chr(9)+"Ctrl+C")
MenuTitle("title2")
MenuItem(14, "item 14", ImageID(0))
Define hSubmenu = OpenSubMenu("sub")
Define FirstMenuID = 15
Define LastMenuID = 50
Define SplitPoint = (LastMenuID - FirstMenuID) / 2 + 1
Define item.MENUITEMINFO
Define i, e
For i = FirstMenuID To LastMenuID
If AddElement(SubMenuItems())
SubMenuItems()\Text = "item " + i
SubMenuItems()\hIcon = hImg
SubMenuItems()\IconW = ImageWidth(0)
SubMenuItems()\IconH = ImageHeight(0)
item\cbSize = SizeOf(MENUITEMINFO)
item\fMask = #MIIM_FTYPE | #MIIM_ID | #MIIM_DATA
item\fType = #MFT_OWNERDRAW
item\wID = i
item\dwItemData = @SubMenuItems()
If i = SplitPoint + FirstMenuID
item\fType | #MFT_MENUBARBREAK
EndIf
InsertMenuItem_(hSubmenu, i - FirstMenuID, #True, item)
EndIf
Next
CloseSubMenu()
MenuTitle("title3")
MenuItem(51, "51" +Chr(9)+"Ctrl+O")
MenuItem(52, "52" +Chr(9)+"Ctrl+O")
MenuItem(52, "53" +Chr(9)+"Ctrl+S")
EndIf
Repeat
e = WaitWindowEvent(1000)
If e = #PB_Event_Menu
Debug EventMenu()
EndIf
Until e = #PB_Event_CloseWindow
DeleteObject_(g_hMenuFont)
EndIf