Silverfrost Forums

Welcome to our forums

Icons in menus, centering captions

29 Mar 2011 4:19 #7985

Does anyone know how to put icons against menu text? Enhanced menus appear to have bitmap capability, but the bitmap looks like it is to be provided instead of the text (the example in FTN95.CHM embeds the text in the bitmap). Windows 7 actually reserves a space for these icons. Ticks (which I have mastered) go in the same reserved space.

The Paint application in Windows 7 uses the radio button facility) also in enhanced menus instead of a tick, and that looks to me to be 2 ways of doing the same job.

Does anyone know how to centre the caption (other than the long-winded way of getting the window dimensions, space-padding the text in the caption, and updating the caption).

Eddie

7 Jun 2013 9:01 #12330

I just thought that I would resurrect this thread, which got no replies at the time, in the hope that someone (I'm hoping that it might be Jalih, who understands these things!) might respond.

Enhanced menus can have a bitmap type. The FTN95.CHM example shows :

fmt='[File[-bm[openbmp],-se,E&xit],Help[About]]'

where -bm uses the bitmap described in the RESOURCES section as openbmp, but this also includes the text for the menu item (in this case 'Open', and the whole bitmap is placed in the position for the text in the menu. In later Windows version, there is a special column in a menu for an icon, and putting the icon in the text column looks odd. Moreover, no matter how carefully one prepares the bitmap, a change in font in Windows (which sometimes happens anyway with a change to a different version of Windows) leaves the menu item looking very strange.

One of my programs contains the following line:

       IA = WINIO@('%mn[[|,Audible warnings[#On,#Off]]]&',
     &     Is_Audible,     Audibility_on_to_off_Function,
     &     Not_Audible,    Audibility_off_to_on_Function)

I would dearly love to be able to write something like:

       IA = WINIO@('%@mn[[|,Audible warnings[#On,#Off]]]&',
     &     Big_ear_bitmap,
     &     Is_Audible,     Audibility_on_to_off_Function,
     &     Not_Audible,    Audibility_off_to_on_Function)

Where some code (such as the @ I have used) flags up that a bitmap should be put against that menu item, in this case Big_ear_bitmap from the RESOURCES. (Should it be 'Big_ear_bitmap'?). A corresponding thing in an enhanced menu might use -im instead of -bm.

Menu bitmaps are always very small, and although most programs seem to have them, Windows 7 Notepad doesn't. I also see that some of the standard apps in Windows have moved away from conventional menus (see, for example, Windows 7 Paint or Wordpad) - towards a layout with a 'ribbon' that it seems pointless to aspire to at this stage.

Eddie

7 Jun 2013 11:22 #12332

When I added this feature to Plato it involved a significant amount of coding at the time.

Unless things have changed, I don't think that I can offer to add this functionality to ClearWin+.

7 Jun 2013 12:27 #12333

Paul,

I remember you saying so somewhere else. I tried looking it up on MSDN, and couldn't follow the explanation there; ditto with a whole bunch of other websites that purported to explain how.

However, I have observed that different folks find certain things easy and certain things difficult, while all that is necessary with the difficult things (sometimes!) is to learn the trick - or better still, to be taught it by someone who finds it easy. My hope is that that there is someone out there who knows the trick for this one, and is willing to share the knowledge.

Eddie

7 Jun 2013 2:17 #12335

This great link explains the different methods for adding icons to menu items.

I should be able to write some example code based on the above, if needed...

7 Jun 2013 2:25 #12337

I have used WM_MEASUREITEM and WM_DRAWITEM in Plato but my gut feeling is that it would take quite a lot of work to add this to the existing ClearWin+ menu syntax.

7 Jun 2013 2:33 #12338

Quoted from PaulLaidler I have used WM_MEASUREITEM and WM_DRAWITEM in Plato but my gut feeling is that it would take quite a lot of work to add this to the existing ClearWin+ menu syntax.

I thought about using the last method described in the article:

  • convert icon to PARGB32 bitmap
  • set menuiteminfo.hbmpItem = PARGB32 bitmap handle
  • call SetMenuItemInfo()
7 Jun 2013 2:46 #12339

OK. There may be some advantage here but it could still be very tricky to integrate this with the existing code in a tidy manner.

7 Jun 2013 4:34 #12342

Paul,

Does this mean not as a format code or format code enhancement, but as a 'modifier' routine, such as:

IA = Icon_Onto_Menu_Item@ ('File|Open', Open_Icon)

with Open_Icon in the RESOURCES? That would be quite clean. It might be better to have:

IA = Icon_Onto_Menu_Item@ ('File|Open', Open_Icon, Open_Icon_Grey)

as automatic greyed-out conversion (like in %ib) takes a lot more effort to get the icons looking like anything other than irregular blobs in their greyed out state - and in a 13x13 icon it would prove very difficult. (It isn't impossible, but it needs some work).

I made a format code suggestion without thinking it through (not that an infinite amount of thought would have helped me).

Eddie

7 Jun 2013 8:36 #12345

My first thought would be to add icon information within %mn but just doing the a feasibility study seems to me to be a large task at the moment.

11 Jun 2013 5:51 #12365

I have looked into the menu bitmap issue and here is the outcome so far.

If you attach a bitmap directly to a Microsoft menu item then either you are limited to a 13x13 bitmap or the whole thing (i.e. including text) has to be a bitmap (as in the existing ClearWin+ enhanced menus).

The alternative is to use MF_OWNERDRAW with WM_DRAWITEM etc. Now that I have got this far, this approach seems do-able given that there is some relevant code already present and I can adapt code imported from Plato.

Basically I plan to add %mo (menu options for the whole window). Initially this will only enable the use of menu icons but later we might add options to specify a background icon or colour etc.. After using %mo once for a given window, %mn[File[OPENICON/Open]] will mean that OPENICON is a (16x16?) icon resource and 'Open' is the following text.

11 Jun 2013 8:45 #12366

Paul,

When you said 16x16 (last line), did you mean 13x13?

If you have a PC with a larger font setting, say 125%, my understanding is that the icon needs to be 17x17, and so on.

I presume (because there were no comments on it when I posted on it in a topic 'DPI Awareness') that no-one else comes across computers with larger font settings. Indeed, I only have once, and that was an Acer laptop that I bought with a very high resolution screen. It is quite easy to select the correct options to display any screen when an application starts: what is much more difficult is to sense that the user has changed the default font setting and then respond to it properly. The mechanisms for setting the 'logical DPI' have changed from XP, and there are issues with the routine SetProcessDPIAware.

It seems to me therefore that following up on your suggestion it is probably worth allowing icons of any size, but to put a note in the documentation that finding the right size icon is the user's responsibility.

Your efforts are appreciated.

Eddie

11 Jun 2013 8:56 #12367

I meant 16x16 which is the icon size in Plato and Visual Studio (I think). For my first attempt I will use 16x16 and then see how I go. There is a whole load of stuff to do with owner draw so I don't want to take on too much to start with. (e.g. Is the item greyed? selected? checked? unchecked? Does it have an icon? What theme to use.)

14 Jun 2013 7:50 #12406

Quoted from PaulLaidler

For my first attempt I will use 16x16 and then see how I go. There is a whole load of stuff to do with owner draw so I don't want to take on too much to start with. (e.g. Is the item greyed? selected? checked? unchecked? Does it have an icon? What theme to use.)

Hi Paul,

If you use the HBMMENU_CALLBACK method instead of full owner draw, then you only need to draw the icon. Everything else is done for you.

Simple MiniBasic example below (See notes in source):

First define simple menu:

HMENU hMenu = _CreateMenu()
HMENU hSubMenu = CreatePopupMenu()
_AppendMenu(hSubMenu,MF_STRING, 1, 'E&xit')
_AppendMenu(hMenu, MF_STRING | MF_POPUP, hSubMenu, '&File')
_SetMenu(hWnd,hMenu)

Then fill in and set the correct MENUITEMINFO structure:

MENUITEMINFO mii
mii.cbSize = len(MENUITEMINFO)
mii.fMask = MIIM_BITMAP | MIIM_ID
mii.hbmpItem = HBMMENU_CALLBACK
mii.wID = 10 ' Use this ID as link to icon resource when drawing

SetMenuItemInfo(hMenu,1,FALSE,&mii)

All that is left to do is to handle the WM_MEASUREITEM and WM_DRAWITEM messages in parent's window procedure:

	case WM_MEASUREITEM
		pointer pmis = *(MEASUREITEMSTRUCT)lParam
		' Make sure there is a room for the icon in menu field.
		if #(MEASUREITEMSTRUCT)pmis <> NULL AND #(MEASUREITEMSTRUCT)pmis.CtlType = ODT_MENU
			#(MEASUREITEMSTRUCT)pmis.ItemWidth += 4
			if #(MEASUREITEMSTRUCT)pmis.ItemHeight < 16 then #(MEASUREITEMSTRUCT)pmis.ItemHeight = 16
		endif
		return TRUE
	case WM_DRAWITEM
		pointer pdis = *(DRAWITEMSTRUCT)lParam
		if #(DRAWITEMSTRUCT)pdis = NULL AND #(DRAWITEMSTRUCT)pdis.CtlType <> ODT_MENU then return S_OK

		' NOTE! Should really use the #(DRAWITEMSTRUCT)pdis.itemID to link the icon resource to menu item.
		HICON hIcon = LoadImageA(0,'Sweeper.ico',IMAGE_ICON,0,0,LR_LOADFROMFILE)
		IF hIcon = NULL then return S_OK

		DrawIconEx(#(DRAWITEMSTRUCT)pdis.hDC,#(DRAWITEMSTRUCT)pdis.rcItem.left - 16,#(DRAWITEMSTRUCT)pdis.rcItem.top + (#(DRAWITEMSTRUCT)pdis.rcItem.bottom - #(DRAWITEMSTRUCT)pdis.rcItem.top - 16) / 2, hIcon, 16, 16, 0, NULL, DI_NORMAL)
		DestroyIcon(hIcon)
		return TRUE

I can write a full featured sample, if it helps... Have fun!

14 Jun 2013 2:23 #12411

Thanks but I have virtually finished the coding and it will allow for larger bitmaps and (later) for additional themes.

p.s. Actually this is the method I have used but within ClearWin+ it turns out to be a lot more detailed.

16 Jun 2013 4:26 #12415

My menu icon implementation ended up using three functions. One function for setting up the icon for menu item and two functions to be passed for window message handler.

func SetMenuIcon(HWND hwnd, uint uItem, BOOL fByPosition, int wID), BOOL
	HMENU hMenu = GetMenu(hwnd)

	MENUITEMINFO mii
	mii.cbSize = len(MENUITEMINFO)
	mii.fMask = MIIM_BITMAP | MIIM_ID
	mii.hbmpItem = HBMMENU_CALLBACK
	mii.wID = wID ' Use this ID as link to icon resource when drawing

	return SetMenuItemInfo(hMenu, uItem, fByPosition, &mii)
endf


func ON_WM_MEASUREITEM(pointer pmis),int
	' Make sure there is a room for the icon in menu field.
	settype pmis, MEASUREITEMSTRUCT
	if #pmis <> NULL OR #pmis.CtlType = ODT_MENU
		#pmis.ItemWidth += 4
		if #pmis.ItemHeight < 16 then #pmis.ItemHeight = 16
	endif
	return TRUE
endf


func ON_WM_DRAWITEM(pointer pdis),int
	settype pdis, DRAWITEMSTRUCT
	if #pdis = NULL OR #pdis.CtlType <> ODT_MENU then return S_OK

	CIcon *myIcon = new CIcon
	myIcon->LoadFromResource(#pdis.itemID + 0)
      
	HICON hIcon = myIcon->Detach()
	IF hIcon = NULL then return S_OK

	DrawIconEx(#pdis.hDC,#pdis.rcItem.left - 16,#pdis.rcItem.top + (#pdis.rcItem.bottom - #pdis.rcItem.top - 16) / 2, hIcon, 16, 16, 0, NULL, DI_NORMAL)
	delete myIcon

	return TRUE
endf
17 Jun 2013 6:04 #12416

Thanks for this Jalih. It is very helpful.

17 Jun 2013 7:49 #12417

I'm designing icons already! Fortunately, MSDN points out that 'deciding to use menu icons doesn't mean that you must have icons for all your menu items. In fact, icons have better effect if they are reserved for only the most important menu items.' and so there aren't hundreds to do!

Eddie

Please login to reply.