Introduction
Since last December, blogs of user interface gurus have been filled with comments about new help features in Office 2007 (aka Office 12), and in particular comments about new super tooltips. I was excited when I first saw them; super tooltips seem to take tooltips to whole new level, making application features much more discoverable to the user, and bridging the gap between popup help and the application's main help documentation. I have been looking forward to when I would have some time to experiment with super tooltips, and I was happy to discover that implementation is easier than I thought; this is mainly due to Eugene Pustovoyt's excellent CPPToolTip class.
First, though, some background on super tooltips. The blog that everyone refers to is Jensen Harris' An Office User Interface Blog from December 2, 2005. Harris includes some stunning screenshots of (beta) versions of super tooltips in Office 2007. Here's a fairly simple one:
| This tooltip gives a brief description of a feature, followed by a concrete example. |
But super tooltips in Office 2007 can include images too:
| This picture makes clear what would probably require a hundred words. |
One of coolest features of new tooltips is that they can provide a link between simple popup and the application's main help system:
| The user now has an opportunity to go from a very concise feature description to jumping directly to the relevant article in the help system, without having to bother with the help index, or problematic search for keyword. |
Another use that Harris says will be natural for super tooltips is to help user understand the status of user interface. For example, it is common to see features that are disabled in an application, but sometimes it is not obvious what has caused them to be disabled:
| This one tooltip does four things: it shows user the status of feature; it explains why feature has that status; it tells user how to enable feature; and it offers an express (F1) way into help system. This means that users will be able to learn new applications very quickly, and will pick up new features with minimal frustration. |
Another example that Harris gives is finding the right dialog. Many users recognize a dialog box and what it does, but have trouble finding how to get to it. Again, using embedded image, super tooltips show a preview of dialog:
Super Tooltip Requirements
From the above summary, I put together the following list of requirements for super tooltip implementation:
- Hot keys to link to documentation: In addition to the obvious F1, it should be possible to specify any function key; pressing function key will cause message to be sent back to tooltip's parent.
- Embedded images: Displaying thumbnail image in the body of tooltip, and smaller image in footer, is the primary requirement.
- Easy, flexible formatting: To use super tooltips widely throughout an application, it must be possible to efficiently generate and modify various formatting elements, such as font weight, underlining, etc. This makes HTML a natural choice for use in super tooltips.
- Programmatic interface to display attributes: The programmer (and possibly the user) should be able to easily change background color, text color, and effects such as gradient colors and shading.
- Customize tooltip elements: To make possible wide variety of tooltips, it should be possible to pick and choose elements that will be included - header, horizontal rule underneath header, image in body, footer, and horizontal rule before footer.
Testing CXSuperTooltip
With these requirements, I put together a demo app that gives me control over various aspects of super tooltip formatting and appearance. I first tried one of the examples that Harris used:
I press F1 and demo app receives registered message WM_PPTOOLTIP_VIRTUAL_KEY_CODE
:
The lParam
and wParam
values contain information set up in call to AddTool()
, so an app will know what tool the message is coming from. In this example, I used IDC_PREVIEW
(the button's resource ID) as the tool ID, and the number '12345' as the key code ID. If this was an app with help file, I would have used the button's help topic ID as the key code ID, to make it easy to call HtmlHelp()
.
I can also click on message string, because it is a hyperlink:
Clicking on hyperlink sends the user-defined message UNM_HYPERLINK_CLICKED
to dialog:
| Notice that hyperlink click also returns the tool ID. The 'F1' message string could have been the string version of the help topic ID. |
Next, I disable the preview button by checking both the checkboxes on the right (Disable Preview button
and Enable tooltip on disabled Preview button
):
Now I display an embedded image in super tooltip:
I can also dynamically choose the style of tooltip displayed, without changing any text strings, by using SetScreenTipScheme()
function. When "normal" tooltips are selected, only header text is displayed:
The above examples do not show all combinations of features possible with the demo app, so I hope you will experiment with demo app and let me know what you think.
Demo App — A Super Tooltip Code Generator
One of the things that took the most time was figuring out the exact HTML code I needed for super tooltip. To make it easy to implement in your code, demo app provides two dynamic clipboard export mechanisms, which give you the starting point for your own super tooltip:
- Copy HTML to Clipboard
This gives you HTML code that is passed to CPPToolTip::AddTool()
as sTooltip
value in PPTOOLTIP_INFO
struct.
<font color=#000000><b>Merge and Center</b>
<indent>
Joins the selected cells into one larger cell and centers
the contents in the new cell.</indent>
<indent>
This is often used to create labels that span
multiple columns.</indent></font><indent size=-50><br>
<hr width=500px color=#69696B></indent>
<table>
<tr>
<td width=20><ilst idres=131 mask cx=16 cy=16 width=100%
height=100%></td><br>
<td><b><font color=#000000>Press F1 for more help</b>
</td></font></tr></table>
- Copy C++ Code to Clipboard
This gives you C++ code that is used to set up SUPER_TOOLTIP_INFO
struct before CXSuperTooltip::AddTool()
is called.
SUPER_TOOLTIP_INFO sti;
sti.bSuperTooltip = TRUE;
sti.nVirtualKeyCode = VK_F1; sti.nKeyCodeId = <insert key code id here>; sti.nIDTool = <IDC_XXXXX>; sti.nSizeX = 212;
sti.pWnd = GetDlgItem(IDC_XXXXX);
sti.strHeader = _T("Merge and Center");
sti.bLineAfterHeader = FALSE;
sti.strBody =
_T("Joins the selected cells into one larger cell and
centers the contents in the new cell.\n")
_T("This is often used to create labels that span multiple columns.\n")
;
sti.strFooter = _T("Press F1 for more help");
sti.nFooterImage = IDB_YYYYY;
sti.bLineBeforeFooter = TRUE;
sti.rgbBegin = RGB(255, 255, 255);
sti.rgbMid = RGB(242, 242, 246);
sti.rgbEnd = RGB(212, 212, 224);
sti.rgbText = RGB(0, 0, 0);
CString strHtml = m_tooltip.AddTool(&sti);
Note
The complete code that is used to generate super tooltips in the demo app can be found in CXSuperTooltipTestDlg::Generate()
.
Implementation Details
HTML Engine
An important thing to understand about CXSuperTooltip
is that it is based on CPPToolTip
, which uses custom HTML rendering engine to display HTML, and not Microsoft browser APIs. This means that you will find some differences in the way HTML looks. For the most part, once you try a few examples, you will have no problems. To implement CXSuperTooltip
, I found it necessary to tweak the CPPToolTip
classes a little bit, and I also applied bug fixes that were reported in CPPToolTip
article forum.
CXSuperTooltip Class
The CXSuperTooltip
class serves as an interface to CPPToolTip
. This means that when you call CXSuperTooltip::AddTool()
in your code, in addition to setting up HTML, CXSuperTooltip
is also calling various CPPToolTip
functions.
Here is function CXSuperTooltip::InitSuper()
, which is called by CXSuperTooltip::AddTool()
:
void CXSuperTooltip::InitSuper()
{
int yMargin = GetSize(CPPToolTip::PPTTSZ_MARGIN_CY);
SetSize(CPPToolTip::PPTTSZ_MARGIN_CY, yMargin/2);
SetSize(CPPToolTip::PPTTSZ_ROUNDED_CX, 0);
SetSize(CPPToolTip::PPTTSZ_ROUNDED_CY, 0);
SetSize(CPPToolTip::PPTTSZ_WIDTH_ANCHOR, 0);
SetSize(CPPToolTip::PPTTSZ_HEIGHT_ANCHOR, 0);
SetBorder(RGB(105,105,107));
SetTooltipShadow(6, 5, 50);
SetNotify();
SetColorBk(RGB(255, 0, 0));
SetMaxTipWidth(XSUPERTOOLTIP_DEFAULT_WIDTH);
SetDelayTime(TTDT_AUTOPOP, 60000);
}
Some other CPPToolTip
functions are called from within CXSuperTooltip::AddTool()
, depending on options specified in SUPER_TOOLTIP_INFO
struct:
struct SUPER_TOOLTIP_INFO
{
SUPER_TOOLTIP_INFO()
{
bSuperTooltip = TRUE;
pWnd = NULL;
nIDText = 0;
nIDTool = 0;
rectBounds = CRect(0,0,0,0);
nBehaviour = 0;
nSizeX = 0;
strHeader = _T("");
strBody = _T("must not be empty");
strFooter = _T("");
bLineAfterHeader = FALSE;
bLineBeforeFooter = FALSE;
nBodyImage = 0;
nFooterImage = 0;
nVirtualKeyCode = 0;
nKeyCodeId = 0;
rgbBegin = RGB(255,255,255);
rgbMid = RGB(242,242,246);
rgbEnd = RGB(212,212,224);
rgbText = RGB(0,0,0);
};
BOOL bSuperTooltip; CWnd * pWnd; UINT nIDText; UINT nIDTool; CRect rectBounds; UINT nBehaviour; int nSizeX; CString strHeader; CString strBody; CString strFooter; BOOL bLineAfterHeader; BOOL bLineBeforeFooter; UINT nBodyImage; UINT nFooterImage; UINT nVirtualKeyCode; UINT nKeyCodeId; COLORREF rgbBegin; COLORREF rgbMid; COLORREF rgbEnd; COLORREF rgbText; };
How To Use
To integrate CXSuperTooltip
class into your app, you first need to add following files to your project:
- CeXDib.cpp
- CeXDib.h
- Monitors.cpp — new in v1.1
- Monitors.h — new in v1.1
- MultiMonitor.cpp — new in v1.1
- MultiMonitor.h — new in v1.1
- PPDrawManager.cpp
- PPDrawManager.h
- PPHtmlDrawer.cpp
- PPHtmlDrawer.h
- PPTooltip.cpp
- PPTooltip.h
- XSuperTooltip.cpp
- XSuperTooltip.h
Next, include header file XSuperTooltip.h in appropriate project files (usually, this will be in the header file for dialog class). Then replace declaration of tooltip control with this:
CXSuperTooltip m_tooltip;
(use whatever variable name already exists).
Now you are ready to start using CXSuperTooltip
. In dialog's OnInitDialog()
function, insert line
m_tooltip.Create(this);
(this line will already be there if you are already using tooltips).
After calling Create()
, you can add controls to tooltip by using CXSuperTooltip::AddTool()
— see code in XSuperTooltipTestDlg.cpp.
If you want to process virtual key click, you should also add entry to message map:
ON_REGISTERED_MESSAGE(WM_PPTOOLTIP_VIRTUAL_KEY_CODE, OnVirtualKey)
and add function
OnVirtualKey()
to dialog class:
LRESULT CXSuperTooltipTestDlg::OnVirtualKey(WPARAM wParam, LPARAM lParam)
{
CString msg = _T("");
CString strMagic = _T("");
if (wParam == 0x4d)
strMagic = _T("MFC magic help message!");
msg.Format(_T("WM_PPTOOLTIP_VIRTUAL_KEY_CODE received from
CPPToolTip: \n\n")
_T(" key code ID = %d (0x%X) %s\n")
_T(" tool ID = %d (0x%X)"),
wParam, wParam, strMagic, lParam, lParam);
AfxMessageBox(msg, MB_OK | MB_ICONINFORMATION);
return 0;
}
In your implementation of
OnVirtualKey()
, you will want to call
HtmlHelp()
or whatever help system your app is using. To facilitate this, you could use help topic id as key code id (
nKeyCodeId
), and call
HtmlHelp()
like this:
TCHAR szPathName[MAX_PATH*2] = { _T('\0') };
::GetModuleFileName(AfxGetInstanceHandle(), szPathName,
sizeof(szPathName)/sizeof(TCHAR)-1);
TCHAR *cp = _tcsrchr(szPathName, _T('.'));
if (cp != NULL)
*(cp+1) = _T('\0');
_tcscat(szPathName, _T("chm"));
if (_taccess(szPathName, 04) == 0)
{
HtmlHelp(AfxGetApp()->m_pMainWnd->m_hWnd, szPathName,
HH_HELP_CONTEXT, wParam);
}
Next add the function PreTranslateMessage(MSG* pMsg)
to your dialog:
BOOL CXSuperTooltipTestDlg::PreTranslateMessage(MSG* pMsg)
{
if (m_tooltip.RelayEvent(pMsg))
return TRUE;
return CDialog::PreTranslateMessage(pMsg);
}
Frequently Asked Questions
- Why use CXSuperTooltip at all? Why not just use CPPToolTip?
You can certainly do that, although you would be duplicating much of the code in CXSuperTooltip
. I recommend that you use PPTooltip.cpp, etc., files that I include with demo app — I have made numerous bug fixes and additions to accommodate super tooltips. - Does XSuperTooltip support Unicode?
Yes. - Can I use XSuperTooltip in non-MFC apps?
Not currently. XSuperTooltip
is dependent on MFC framework and classes. - When I try to build the demo app, I get the linker error
LINK : fatal error LNK1104: cannot open file "mfc42u.lib" Error executing link.exe
. How can I fix this?
The default installation options of Visual C++ v6.0 don't install the Unicode libraries of MFC, so you might get an error that mfc42u.lib or mfc42ud.lib cannot be found. You can fix this either by installing Unicode libs from the VC++ install CD, or by going to Build | Set Active Configuration and selecting one of the non-Unicode configurations. - I'm trying to use standard HTML in my super tooltip, but it doesn't work. What is wrong?
XSuperTooltip
is based on custom HTML rendering engine, not on official Microsoft browser APIs. Take a look at the code, you might be able to implement missing feature yourself. - Is this the final version of XSuperTooltip?
Probably not. Office 2007 has not yet been released, and so super tooltips may undergo more changes in functionality and appearance. When Office 2007 is completed (which some people are predicting will happen later this year), I will review any changes, and decide what to do. In the meantime, please feel free to experiment with XSuperTooltip
, and let me know if you think there are features that would make it better. - Can we use XSuperTooltip in our shareware/commercial app?
Yes, you can use XSuperTooltip
without charge or license fee. It would be nice to acknowledge my Copyright in your About box or splash screen, but this is up to you.
Acknowledgments
Please refer to the following articles for more information about controls I used to build the demo app:
Revision History
Version 1.1 — 2006 August 4
- Fixed
delete
's in PPDrawManager.cpp and CexDib.cpp — thanks to Johnny_Boy for finding this. - Many tweaks to the UI — thanks to Geert van Horrik and Alisdair W for helping with these:
- "Office 2007 blue" color, with text color of RGB(76, 76, 76)
- border color is now RGB(118, 118, 118)
- corners are now rounded
- shading has been tweaked
- margin has been reduced
- horizontal rule is now 3D, and color is taken from background
- added fadeout if system option set to fade
- Fixed static build problem ("Use MFC in a static library") — thanks to bolivar123 for reporting on fix.
- Fixed tooltip behavior — it is now much less twitchy, and mouse may be moved anywhere within control or tooltip without closing tooltip.
- Added hyperlink support for "Press F1 ...".
- Because the mouse may be moved into tooltip now, the tooltip's context menu was removed — "help" message can be hyperlink and may be directly clicked, without having to use the keyboard.
- Added option to show super tooltip, normal tooltip (displays only header text), or nothing.
- Added
SetCapture()
to fix problem where tooltip would not be hidden if cursor moved off bottom of tooltip. - Tooltip is now repositioned to top of control if it would be displayed off bottom of screen.
Version 1.0 — 2006 July 31
Usage
This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.