Menu Icons
Posted: Thu Apr 20, 2006 2:13 am
Hi,
a while ago I revamped some code for drawing icons in menus, but made it far too complex and awkward to use.
In need of something much simpler, I have reworked the library into something far more streamlined and easy to use.
Indeed there is only one command which replaces the PB MenuItem() command. Note, the parameter iconhandle is optional.
Example program:
Now the include file which must reside in the same directory as the above example program for the example to run.
Note the use of colours in the example program. Menu colours can easily be changed by changing a few constants in the include file.
The thing about this is that it uses proper icon images etc. The usual alternative is to convert to a bitmap first and then 'cheat' by setting the menuitem's checked and unchecked bitmaps etc. but this just doesn't look as 'smooth' imo.
I hope it's useful in some way.
**Note: this particular library will not work with popup menus. It would need a few minor adjustments first, but it's not something which I am in need of!
a while ago I revamped some code for drawing icons in menus, but made it far too complex and awkward to use.
In need of something much simpler, I have reworked the library into something far more streamlined and easy to use.
Indeed there is only one command
Code: Select all
AddMenuIcon(WindowID(#Window), MenuItem, Text$, [iconhandle])
Example program:
Code: Select all
XIncludeFile "MenuIcons.pbi"
;Load a couple of icons. Would normally use a resource file etc.
iconopen=LoadImage_(0,"open.ico",#IMAGE_ICON,0,0,#LR_LOADFROMFILE)
iconsave=LoadImage_(0,"save.ico",#IMAGE_ICON,0,0,#LR_LOADFROMFILE)
If OpenWindow(0, 200, 200, 200, 100, "AddMenuIcon Example")
CreateMenu(0, WindowID(0))
MenuTitle("File")
AddMenuIcon(WindowID(0),1, "New"+Chr(9)+Chr(9)+"Ctrl+N") ;No icon provided
AddMenuIcon(WindowID(0),2,"&Open"+Chr(9)+Chr(9)+"Ctrl+O", iconopen)
AddMenuIcon(WindowID(0),3,"Save"+Chr(9)+Chr(9)+"Ctrl+S", iconsave)
AddMenuIcon(WindowID(0),4,"&Save As..."+Chr(9)+"Ctrl+A") ;No icon provided
MenuBar()
AddMenuIcon(WindowID(0),5,"Exit") ;No icon provided
DisableMenuItem(0,3,1)
DisableMenuItem(0,4,1)
Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow
EndIf
;Tidy up!
DestroyIcon_(iconopen)
DestroyIcon_(iconsave)
End
Code: Select all
;Menu icons.
;Stephen Rodriguez 2006.
;Purebasic 4 beta 11.
;This small 'include' file allows for some simple customising of menus.
;Whilst it uses full 'owner' drawn methods, it nevertheless only allows a developer to add
;icons to individual menu items. This is to keep things simple etc.
;Other features, such as background colours etc. can be set by changing the appropriate constants
;in this include file.
#DT_END_ELLIPSIS = $8000
#MIIM_STATE = 1
;******************************************************************************************
;CHANGE THE FOLLOWING CONSTANTS TO MAKE GLOBAL CHANGES etc.
#Menu_SelectedTextColour=#Blue
#Menu_SelectedBackColour=$94FF6B
#Menu_SelectedBorder=1 ;Thickness of border.
#Menu_IconSize=20 ;Icons are dynamically resized.
#Menu_ItemHeight=20
#Menu_TextColour=#Red
#Menu_BackColour=$19F0E6
Global Menu_Font=LoadFont(#PB_Any, "arial", 10)
;******************************************************************************************
;The following procedure takes responsibility for drawing the menu items.
Procedure MENUICONS_CallbackProc(hWnd, uMsg, wParam, lParam)
Protected result, prop$, *lpmis.MEASUREITEMSTRUCT, *lpdis.DRAWITEMSTRUCT, menuitinf.MENUITEMINFO, itemtext$
Protected rc.RECT, hFontOld, hdc, size.SIZE, pen, brush, oldpen, oldbrush, icontop, borderpen
Protected temp$,len,i, tm.TEXTMETRIC
Select uMsg
Case #WM_MEASUREITEM ;This message is called when the menu / submenu is first created and is used to size the menu items.
*lpmis = lParam
If *lpmis\CtlType = #ODT_MENU ;Don't want this to run for an owner drawn button etc!
itemtext$=Space(255)
GetMenuString_(GetMenu_(hWnd), *lpmis\itemid, @itemtext$,255,#MF_BYCOMMAND)
;Working out the rectangle width is complicated by the possible inclusion of tab characters.
itemtext$=RemoveString(itemtext$,"&")
For i = 1 To Len(itemtext$)
If Mid(itemtext$,i,1)=Chr(9)
len=8*(len/8+1)
Else
temp$+Mid(itemtext$,i,1)
len+1
EndIf
Next
hdc = GetDC_(hWnd)
hFontOld = SelectObject_(hdc,FontID(Menu_Font))
GetTextExtentPoint32_(hdc,temp$,Len(temp$),@size)
GetTextMetrics_(hdc,tm)
*lpmis\itemWidth = size\cx+(len-Len(temp$))*tm\tmAveCharWidth+#Menu_IconSize+#Menu_IconSize/2+2*#Menu_SelectedBorder-30
*lpmis\itemHeight = #Menu_ItemHeight
;If the height chosen by the developer is too small for the text then we need to adjust it.
If tm\tmHeight+2*#Menu_SelectedBorder > *lpmis\itemHeight
*lpmis\itemHeight = tm\tmHeight+2*#Menu_SelectedBorder
EndIf
If #Menu_IconSize+2*#Menu_SelectedBorder > *lpmis\itemHeight
*lpmis\itemHeight = #Menu_IconSize+2*#Menu_SelectedBorder
EndIf
SelectObject_(hdc,hFontOld)
ReleaseDC_(hWnd,hdc)
result = #True
EndIf
Case #WM_DRAWITEM
*lpdis = lParam
If *lpdis\CtlType = #ODT_MENU ;Don't want this to run for an owner drawn button etc!
itemtext$=Space(255)
GetMenuString_(GetMenu_(hWnd), *lpdis\itemid, @itemtext$,255,#MF_BYCOMMAND)
hdc = *lpdis\hDC
SetBkMode_(hdc,#TRANSPARENT)
hFontOld = SelectObject_(hdc,FontID(Menu_Font))
GetTextExtentPoint32_(hdc,@itemtext$,Len(itemtext$),@size)
If *lpdis\itemState & #ODS_SELECTED
;Check to see if the menu item is disabled.
menuitinf\cbSize = SizeOf(MENUITEMINFO)
menuitinf\fMask = #MIIM_STATE
GetMenuItemInfo_(GetMenu_(hWnd), *lpdis\itemid, #False, @menuitinf)
If menuitinf\fState & 1 = 0
SetTextColor_(hdc,#Menu_SelectedTextColour)
brush=CreateSolidBrush_(#Menu_SelectedBackColour)
If #Menu_SelectedBorder > 0
borderpen=CreatePen_(#PS_SOLID, #Menu_SelectedBorder, 0)
Else
borderpen=GetStockObject_(#NULL_PEN)
EndIf
oldbrush = SelectObject_(hdc,brush)
oldpen=SelectObject_(hdc,borderpen)
Rectangle_(hdc, *lpdis\rcItem\left, *lpdis\rcItem\top, *lpdis\rcItem\right, *lpdis\rcItem\bottom)
icontop = (*lpdis\rcItem\top+*lpdis\rcItem\bottom-#Menu_IconSize)/2
If *lpdis\itemData ;Only draw icon if one was specified by the developer!
DrawIconEx_(hdc,*lpdis\rcItem\left+3,icontop,*lpdis\itemData,#Menu_IconSize,#Menu_IconSize,0,0,#DI_NORMAL )
EndIf
*lpdis\rcItem\top=(*lpdis\rcItem\top+*lpdis\rcItem\bottom-size\cy)/2
*lpdis\rcItem\right-3
*lpdis\rcItem\left+#Menu_IconSize+#Menu_IconSize/2
DrawText_(hdc, @itemtext$, Len(itemtext$), *lpdis\rcItem,#DT_END_ELLIPSIS|#DT_EXPANDTABS)
EndIf
Else
;Check to see if the menu item is disabled.
menuitinf\cbSize = SizeOf(MENUITEMINFO)
menuitinf\fMask = #MIIM_STATE
GetMenuItemInfo_(GetMenu_(hWnd), *lpdis\itemid, #False, @menuitinf)
If menuitinf\fState & 1 = 0
SetTextColor_(hdc,#Menu_TextColour)
Else
SetTextColor_(hdc,$AEAEAE)
EndIf
brush=CreateSolidBrush_(#Menu_BackColour)
borderpen=GetStockObject_(#NULL_PEN)
oldbrush = SelectObject_(hdc,brush)
oldpen=SelectObject_(hdc,borderpen)
Rectangle_(hdc, *lpdis\rcItem\left, *lpdis\rcItem\top, *lpdis\rcItem\right+#Menu_SelectedBorder, *lpdis\rcItem\bottom+#Menu_SelectedBorder)
icontop = (*lpdis\rcItem\top+*lpdis\rcItem\bottom-#Menu_IconSize)/2
If *lpdis\itemData ;Only draw icon if one was specified by the developer!
DrawIconEx_(hdc,*lpdis\rcItem\left+3,icontop,*lpdis\itemData,#Menu_IconSize,#Menu_IconSize,0,0,3)
EndIf
*lpdis\rcItem\top=(*lpdis\rcItem\top+*lpdis\rcItem\bottom-size\cy)/2
*lpdis\rcItem\right-3
*lpdis\rcItem\left+#Menu_IconSize+#Menu_IconSize/2
DrawText_(hdc, @itemtext$, Len(itemtext$), *lpdis\rcItem,#DT_END_ELLIPSIS|#DT_EXPANDTABS)
EndIf
;Tidy up.
SelectObject_(hdc,oldpen)
SelectObject_(hdc,oldbrush)
SelectObject_(hdc,hFontOld)
DeleteObject_(brush) : DeleteObject_(borderpen)
result = #True
EndIf
Case #WM_DESTROY
;Free font.
FreeFont(Menu_Font)
;Remove the window property.
prop$="MENUICONS_oldproc"
result = GetProp_(hwnd, @prop$)
RemoveProp_(hWnd, @prop$)
If result : ProcedureReturn CallWindowProc_(result, hWnd, uMsg, wParam, lParam) : EndIf
EndSelect
If result=0
prop$="MENUICONS_oldproc"
result = CallWindowProc_(GetProp_(hwnd, @prop$), hWnd, uMsg, wParam, lParam)
EndIf
ProcedureReturn result
EndProcedure
;The following procedure adds the given menu item to the list of items to be 'owner drawn'.
Procedure AddMenuIcon(hWnd, item, text$, icon=0)
Protected prop$
MenuItem(item, text$)
If ModifyMenu_(GetMenu_(hWnd),item,#MF_BYCOMMAND|#MF_OWNERDRAW,item,icon)
prop$="MENUICONS_oldproc"
If GetProp_(hWnd, @prop$)=#Null
SetProp_(hWnd, @prop$, GetWindowLong_(hWnd, #GWL_WNDPROC))
SetWindowLong_(hWnd, #GWL_WNDPROC, @MENUICONS_CallbackProc())
EndIf
EndIf
EndProcedure
The thing about this is that it uses proper icon images etc. The usual alternative is to convert to a bitmap first and then 'cheat' by setting the menuitem's checked and unchecked bitmaps etc. but this just doesn't look as 'smooth' imo.
I hope it's useful in some way.

**Note: this particular library will not work with popup menus. It would need a few minor adjustments first, but it's not something which I am in need of!