C/C++ Users Journal November, 2004
In the best of worlds, your applications will look nice, but without too much work on your part. Luckily, it is possible to have your GUI cake and eat it too by having cool-looking, user-friendly programs that don't require a lot of coding effort. For instance, everybody loves tooltips. They're cute and show up just when users need them. Unfortunately, controls don't have a tooltip_text() property like that of the text() property. There are reasons for this, among them that you might want different tip texts depending on where the mouse is positioned.
By default, if a control has a string resource with its IDthat's its tooltip. It's that simple (see Figure 1). What's best is that you don't have to fill the tooltip strings yourself. You can focus on writing code, while the UI folks use the Resource Editor and fill the tooltips where needed.
Moreover, this gives you consistency. For instance, you might have a control with a given ID (ID_empl_address, for instance) sited in multiple dialogsits tooltip is always the same (the string resource having the ID_empl_address id).
But because real life isn't always this easy, you can override this. For a dialog, you can specify what tooltip is to be shown for any of its controls:
You can use 0 for ctrl_id to set the tooltip for all controls on a dialog. For instance, calling tip_text(0,no_tip_text) disables tooltips for this dialog; see Listing 1, which results in Figure 2.
You might want to have multiple types of tooltips at oncesay, the regular tooltip, a balloon tooltip (the one that comes with Windows 2000), and one with a pink background and the Courier New font. Here's how: Behind the scenes, tooltip controls are windows. If you want multiple types of tooltips, you need to create each. Then, you give each tooltip control a name. Finally, assign each control to a certain name. Listing 2 illustrates this, and Figure 3 is the outcome.
Tooltips work for menus, too. Your application frame menu and any menu you show on a dialog allow for tooltips in the same manner (if there is a string resource corresponding to a certain menu item ID, it becomes its tooltip).
Unfortunately, there's no standard hyperlink control. However, it's easy to make oneyou subclass the label class, override the paint events so that it prints like a hyperlink, and monitor for mouse movement. Pretty much like class hyper_link. To make things easier, hyper_links (like splitters) implement automapping. This means that all labels with the ID between ID_hyperlink1 and ID_hyperlink15 become hyper_links at runtime. Thus, you can specify at dialog design time the text for a hyperlink. By default, the link is the text itself; see Figure 4. You can override it by using the hyper_ link::set_link(str) method. Or you can set both text and link at design time, separating them by a carriage return. For instance:
Click Me\nhttp://www.torjo.com
shows a hyperlink with text "Click Me". When you click it, it goes to "http://www .torjo.com."
You can also have a picture control act as a hyperlink; give it an ID from ID_hyperlink1 to ID_hyperlink15 (picture controls are still labels under the hood, but with a different creation style). However, you have to set the link manually, since you can't do it at design time (the Resource Editor won't let you).
There are times when you have a small HTML text to print out, but are not in the mood to do so manually. Furthermore, the IE browser control is usually too much overhead and more trouble than it's worth. Clearly, what you need is a "lite" HTML controlone that supports a small HTML subset for setting colors, fonts (size and name), hyperlinks, super/subscripts, italics, bold, and underline.
What you can do is subclass the label control and implement automapping. At runtime, all labels with an ID of ID_litehtml1 to ID_litehtml15 become lite HTML controls (lite_html class). Setting the HTML text is straightforwardusing the text() method. You can set it either at design time, and/or at runtime, depending on your application needs. Table 1 lists features you can use. For changing the font, the attributes are: color="color string," bgcolor="color string," size="adjust size," and face="font name." Figure 5 is a simple example; more are available at http://www.torjo.com/win32gui/. (Thanks to Hans Dietrich, who developed this control for MFC. I ported it to win32gui.)
Most of the time, buttons look pretty dull. However, putting a bitmap next to them gives them a professional look. All you need to do is to specify which bitmap to use and, in the rest, treat it like a regular button. Here's how: First, I have extended the button class and implemented automapping. At runtime, I subclass all buttons. When first created, I check whether there's a bitmap or icon resource with the same ID. If so, I load the desired bitmap or icon and show it next to the text; otherwise, let the default behavior take over. Once more, there's virtually no work involvedjust let the UI guys create the bitmaps/icons in the Resource Editor (much like with the tooltips).
In the process, you also gain a consistent look-and-feel: If a button with a certain ID is sited on more than one dialog, it has the same bitmap assigned to it. It works fine, especially for "OK" and "Cancel" buttons. As long as they have the usual IDOK, IDCANCEL IDs, they will look the same in all dialogs. You usually have a few buttons that have the same meaningAdd, Del, Edit, and the like. Since users expect buttons to look uniform throughout the application, just give all Add buttons a fixed ID, such as ID_add (likewise for same for Del and Edit), have someone create three bitmaps for them, use the Resource Editor and add them, and that's itno coding required. As a bonus, this even works for checkboxes and radio buttons that have the Push-like property set.
Just in case you don't like the defaults, you can easily change the image:
In this case, kudos to Ravi Bhavnani for developing FooButton for MFC, which I also ported to win32gui.
The msg_box (or ::MessageBox) control is generally limited, giving you only a few combinations to choose from. More annoying is that when you get an error, you cannot copy the message into the clipboard or resize message boxes. Listing 3 lets you make your own message box; Figure 6 is the outcome.
Although they can require lots of code to implement, tabs do provide a nice way of splitting information. The common scenario is when the tab control has some children, and only one is visible at a time. When users change the tab, another child is shown (MFC's Property Page is a tab control which does just this).
With this in mind, I've enhanced the tab_ctrl: class tab_dialog. You can create tab_dialogs at dialog design timejust create a tab_ctrl and assign it an ID from ID_tabdialog1 to ID_tabdialog10. You can then create subdialogs at design time (just create then as you would create a dialog-on-dialog, and place them inside them tab dialog), and/or at runtime.
The "children" of the tab control are created on the tab's parent, not on the tab itself. This brings two benefits:
Additionally, when the tab is resized, it automatically resizes all children to take all of its client area.
Another win32gui goodie is the window_base::window_rect function. Because lots of people complained about the inconsistency of ::GetWindowRect (resulting rectangle is relative to screen) and ::MoveWindow (rectangle is relative to parent) functions, I've designed win32gui so that when you request window_rect, you specify an extra parameterwhich rectangle to retrieve:
Or, you can get the window rectangle relative to another window: w->window_rect(other_w).
Dealing with the mouse is simple:
This is a big help when you're deciding to show context menus. Listing 4 (taken from the menus_and_rebars example) demonstrates how you can show different context menus depending on your cursor position.
While still being efficient, win32gui control classes do provide real OO wrappers over standard controls. Here's what I've implemented so far: button, check_box, radio_button, group_box, edit, multi_edit, label (or static_ctrl), month_cal_ctrl, date_time_ctrl, animate_ctrl, progress_bar, slider_ctrl (or track_bar), spin_button (or updown_ctrl), scroll_bar, tab_ctrl, combo_box, list_box, splitter, header_ctrl, list_ctrl, tree_ctrl, rebar, toolbar, status_bar, hyper_link, lite_html, bitmap_button, tab_dlg, and tooltip_ctrl.
Finally, recall that win32gui is a portable library that currently works with Visual C 7.1, como4.4alfa+, and GCC 3.3+. I intend to port it to other modern compilers, time permitting. Still, I've wanted a simple GUI application that would let me test a C++ project for portability across a set of compilersjust drag-and-drop a set of C++ files and/or directories, select a compiler (or set of compilers), click Build, and it compiles and builds those files using that compiler.
That's exactly what CrossBuilder does. CrossBuilder is a new project at http://www .torjo.com/cb/. It uses win32gui, and the first version offers a lot of features with a small amount of code. I will gradually add features as needed.