Radial context menu ...
... with tips
Mouse gesture with a graphical user interface.
Some time ago, as I was looking for some kind of mouse gesture add-on for my web browser, I came across this web site. There, one can get an add-on for the browsers Firefox and Mozilla, which implements mouse gestures with a graphical front end. I was slightly confused by this fact - 'Mouse gesture with GUI?', I thought. What could this look like? I took a look at the screenshots and was immediately enthused. The add-on merged the advantages of context menus with that of mouse gestures in a clever way: fast execution of commands as with mouse gestures were combined with an easy to understand context menu. In this way one learns the often used gestures very fast while one still has access to the gestures one doesn't recall at any time. Especially less experienced users will profit from this concept. For instance, my wife and my kids won't browse the web without this extension anymore . So if you're using Firefox or Mozilla, go and get your copy of this real great extension!
I planned to implement this ingenious concept for the use with some of my projects for a long time, but had too less free time. Until now...
How it works
As soon as the user presses the right mouse button, a round menu pops up, displaying up to eight icons. The user now may move the mouse in one of eight directions. As soon as (s)he releases the right mouse button, the function below the mouse cursor will be executed. Since mouse gestures don't work precisely on pixels, the radial menu will follow the mouse cursor on demand. The cursor also doesn't need to be exactly over the icon to select a function. To find out which function to use, the menu checks all points on a thought line from the current cursor position to the center of the radial menu. If the line intersects an icon's area, then it is assumed that the user meant this icon. In this way fast acting professionals will get the right result, too. For users who are new to your program and/or can't recall every mouse gesture (or even don't know what the icon stands for), the menu pops up some kind of tool tips after a short delay time. That way even inexperienced users or users who don't like mouse gestures will quickly get along with this concept.
To build up more complex menus (with more than 8 items), one can build up submenus. That way one can implement gestures such as 'right, then up', too.
Using the code
The radial context menu is implemented to watch for the right mouse button. To activate such a menu, you will need to install a handler for the
WM_RBUTTONDOWN window message. The radial context menu object can either be instantiated in this handler method, in the constructor of your
CWnd-derived class, or in the
OnCreate() handler method.
The size of the icons, the colors and fonts to use should be set up before one sets up the menu items themselves. By default the radial menu will use the system colors and fonts as well as an icon size of 16x16 pixels.
// methods for configuration
// Set background color.
CRadialContextMenu & SetBkColor(COLORREF clr = ::GetSysColor(COLOR_BTNFACE));
// Set icon size.
CRadialContextMenu & SetIconSize(CSize size = CSize(16, 16));
CRadialContextMenu & SetIconSize(int cx, int cy);
// Set font for tips
CRadialContextMenu & SetTipFont(CFont * pFont);
// Set colors for the tips
CRadialContextMenu & SetTipColors(COLORREF color1, COLORREF color2,
COLORREF textcolor = ::GetSysColor(COLOR_BTNTEXT));
Having set up the outwardness, one might go on to set up the menu items.
// methods for setting menu items
// Many projects support CFrameWnd or derived window classes that
// own at least one toolbar. To simplify the setup of the radial
// context menu, you might add these toolbars to the menu. You
// don't need to specify an icon then.
CRadialContextMenu & AddToolBar(CToolBar * pBar);
// Note on CRadialContextMenu::EIconPosition:
// The enumeration describes all possible positions for icons. It starts
// with ICON_TOP und goes clockwise to ICON_TOPRIGHT,
// ICON_RIGHT, ICON_BOTTOMRIGHT and so on.
// Tip texts are always optional. Normally the menu tries to locate
// the tips by browsing the string resources for a string with the
// same ID as given as an argument.
// Use this overload only if you have provided at least one toolbar
// that contains the icons.
CRadialContextMenu & SetMenuItem(UINT uID, EIconPosition pos,
const CString & tip = "");
// Use this to provide an icon by yourself.
CRadialContextMenu & SetMenuItem(UINT uID, HICON hIcon,
EIconPosition pos, const CString & tip = "");
// Use this to set a submenu.
CRadialContextMenu & SetMenuItem(CRadialContextMenu * pSubmenu,
HICON hIcon, EIconPosition pos,
const CString & tip = "");
// You might enable/disable items by either
// the command id or the position in the menu
CRadialContextMenu & EnableMenuItem(UINT uID, bool bEnable = true);
CRadialContextMenu & EnableMenuItem(EIconPosition pos, bool bEnable = true);
Now one might call this method.
bool TrackRadialMenu(CWnd * pReceiver);
to pop up the radial context menu. The method won't return unless the user releases the right mouse button or presses the ESC key. The return value indicates whether the user selected a valid item. If it returns
true, then a
WM_COMMAND message was sent to the window given as the argument.
Thus an exemplary implementation of a
WM_RBUTTONDOWN handler might look like this:
void CMyOwnWindow::OnRButtonDown(UINT nFlags, CPoint point)
// In this small example we use standard colors and
// standard icon size and font.
- July, 20 2005 - first implementation.