Step 10: Adding decorations
The only changes in Step 10 are in the InitMainWindow function. But
these changes let you make your application more attractive and easier and more intuitive
to use. In this step, you'll add a control bar with bitmap button gadgets and a status bar
that displays the current menu choice. You can find the source for Step 10 in the files
STEP10.CPP and STEP10.RC in the directory EXAMPLES\OWL\TUTORIAL.
There are four main changes in this step:
Changing the main window
Changing from a TFrameWindow to a TDecoratedFrame is quite easy.
Because TDecoratedFrame is based on TFrameWindow, a decorated frame can be used just about
anywhere that a regular frame window is used. In this case, just create a TDecoratedFrame
and pass it as the parameter to the SetMainWindow function.
Even the constructors of the TFrameWindow and TDecoratedFrame are
alike. The only difference is the fourth parameter, which wasn't being used anyway. The
fourth parameter for TFrameWindow is a bool that tells the frame window whether it
should shrink to the size of its client window.
The fourth parameter for TDecoratedFrame is also a bool. This
parameter indicates whether the decorated frame should track menu selections. Menu
tracking displays a text description of the currently selected menu choice or button in a
message bar or status bar. If you specify true for this parameter, you must supply
a message or status bar for the window. If you don't, your application will crash the
first time it tries to send a message to the message or status bar.
If you're using a status bar, you must include the resources for it
in your resource file. These resources are contained in the file STATUSBA.RC in the
INCLUDE\OWL directory.
The only other difference is that the decorated frame requires some
preparation, such as adding decorations like the control bar and status bar, before it can
become the main window. So instead of constructing and setting the window in one step, you
must construct the window, prepare it, then set it as the main window.
Creating the status bar
Status bars are created using the TStatusBar class. TStatusBar is
based on the TMessageBar class, which is itself based on TGadgetWindow. Both message bars
and status bars display text messages. But status bars have more options than message
bars. For example, you can have multiple text gadgets, styled borders, and mode indicators
(such as Insert or Overwrite mode) in a status bar.
The TStatusBar constructor takes five parameters, although you only
use the first two. The rest of the parameters take on their default values:
| The first parameter is a pointer to
the status bar's parent window. In this case, use frame, which is the pointer to the
decorated frame window constructed earlier.
|
| The second parameter is a
TGadget::TBorderstyle enum. It can be one of None, Plain, Raised, Recessed, or
Embossed. This parameter determines the style of the status bar. This parameter defaults
to Recessed.
|
| The third parameter is a
TModeIndicator enum. It determines the keyboard modes that the status bar should
show. These indicators can be one or more of ExtendSelection, CapsLock, NumLock,
ScrollLock, Overtype, and RecordingMacro. This parameter defaults to 0, meaning to
indicate no keyboard modes.
|
| The fourth parameter is a TFont *.
This contains the font that should be used in the status bar. This defaults to
TGadgetWindowFont.
|
| The fifth parameter is a TModule *.
It defaults to 0.
|
Here is the status bar
constructor:
TStatusBar* sb = new TStatusBar(frame, TGadget::Recessed);
Once the status bar is created, it is
ready to be inserted into the decorated frame. This is described below.
Creating the control bar
Creating the control bar is more involved than creating the status
bar. You first construct the actual TControlBar object. Then you create the gadgets that
make up the controls on the bar and insert them into the control bar.
Constructing TControlBar
The TControlBar constructor takes four parameters, although you need
to use only the first parameter here. The rest of the parameters take on their default
values:
Here is the control bar
constructor:
TControlBar *cb = new TControlBar(frame);
cb->EnableFlatStyle(); // Enable the new flat look of toolbar buttons
Building button gadgets
Button gadgets are used as control bar buttons. They associate a
bitmap button with an event identifier. When the user presses a button gadget, it sends
that event identifier. You can set this up so that pressing a button on the control is
just like making a choice from a menu. In this section, you'll see how to set up buttons
to replicate each of your current menu choices.
Button gadgets are created using the TButtonGadget class. The
TButtonGadget constructor takes six parameters, of which you need to use only the first
three:
| The first parameter is a reference
to a TResId object (see the note on page 43 regarding
the TResId class). This should be the resource identifier of the bitmap you want on the
button. There are no real restrictions on the size of the bitmap you can use in a button
gadget. There are, however, practical considerations: the control bar height is based on
the size of the objects contained in the control bar. If your bitmap is excessively large,
the control bar will be also.
|
| The second parameter is the gadget
identifier for this button gadget. Usually the gadget identifier, event identifier, and
bitmap resource identifier are the same. For example, the button gadget for the File New
command uses a bitmap resource called CM_FILEOPEN, has the gadget identifier CM_FILEOPEN,
and posts the event CM_FILEOPEN. The bitmap is given the same identifier in the resource
file as the event identifier. This makes it a little easier on you when working with the
code. This is not a rule, however, and you can name the bitmap and event identifier
whatever you like. The only stipulation is that the event identifier must be defined and
have some sort of processing enabled and the resource identifier must be valid. You should also notice that there are a number of entries in the application's
string resource table that have the same IDs as the gadgets and events. When a string
exists with the same identifier as a button gadget, that string is displayed in the status
bar when the gadget is pressed.
|
| The third parameter is a TType enum.
This indicates what type of button this is. There are three possible button types,
Command, Exclusive, and NonExclusive. In this application, all the buttons are command
buttons. This parameter defaults to Command.
|
| The fourth parameter is a bool
indicating whether the button is enabled. By default this parameter is false.
|
| The fifth parameter is a TState enum.
This parameter indicates the initial state of the button, and can be Up, Down, or
Indeterminate. This parameter defaults to Up.
|
| The sixth parameter is a bool
that indicates the repeat state of the button. If the repeat state is true, the
button repeats when it is pressed and held. By default, this parameter is false.
|
Separator gadgets
There is another type of gadget commonly used when constructing
control bars, called a separator gadget. Normally gadgets in a control bar are right next
to each other. A separator gadget provides a little bit of space between two gadgets. This
lets you separate gadgets into groups, place them in predetermined spots on the control
bar, and so on.
Separator gadgets are contained in the TSeparatorGadget class. This
is a simple class that takes a single int parameter. By default the value of this
parameter is 6. This parameter indicates the number of pixels of space the separator
gadget should take up.
Inserting gadgets into the control bar
Once your gadgets are constructed, you need to insert them into the
control bar. The control bar can take gadgets because it is derived from the class
TGadgetWindow. TGadgetWindow provides the basic functionality that lets you use gadgets in
a window. TControlBar refines that functionality, producing a control bar.
You can insert gadgets into the control bar using the Insert
function. This version of the Insert function is inherited by TControlBar from
TGadgetWindow (later you'll use another version of this function contained in
TDecoratedFrame). This function takes three parameters, although you need to use only the
first parameter in the tutorial application:
In the tutorial
application, constructing the gadgets and inserting them into the control bar is
accomplished in a single step. Here is the code where the gadgets are inserted into the
control bar:
cb->Insert(*new TButtonGadget(CM_FILENEW, CM_FILENEW,
TButtonGadget::Command));
cb->Insert(*new TButtonGadget(CM_FILEOPEN, CM_FILEOPEN,
TButtonGadget::Command));
cb->Insert(*new TButtonGadget(CM_FILESAVE, CM_FILESAVE,
TButtonGadget::Command));
cb->Insert(*new TButtonGadget(CM_FILESAVEAS, CM_FILESAVEAS,
TButtonGadget::Command));
cb->Insert(*new TSeparatorGadget);
cb->Insert(*new TButtonGadget(CM_PENSIZE, CM_PENSIZE,
TButtonGadget::Command));
cb->Insert(*new TSeparatorGadget);
cb->Insert(*new TButtonGadget(CM_ABOUT, CM_ABOUT,
TButtonGadget::Command));
Notice that the button gadgets replicate
the menu commands you already have. This provides an easy way for the user to access
frequently used menu commands. Of course, you aren't restricted to using gadgets in a
control bar as substitutes or shortcuts for menu commands. Using the TType parameter, you
can set up gadgets on a control bar to work like radio buttons (by using Exclusive with a
group of gadgets), check boxes (using NonExclusive), and so on.
Inserting objects into a decorated frame
Now that you've constructed the decorations for your TDecoratedFrame
window, all you need to do is insert the decorations into the window and make the window
the main window.
Inserting decorations into a decorated frame is similar to inserting
gadgets into a control bar. The TDecoratedFrame::Insert function takes two parameters:
Here is the code for
inserting the decorations into the decorated frame:
// Insert the status bar and control bar into the frame
frame->Insert(*sb, TDecoratedFrame::Bottom);
frame->Insert(*cb, TDecoratedFrame::Top);
Once you've inserted the decorations into
the frame, the last thing you have to do is set the main window to frame and set up the
menu:
// Set the main window and its menu
SetMainWindow(frame);
GetMainWindow()->AssignMenu("COMMANDS");
Where to find more information
Here's a guide to where you can find more information on the topics
introduced in this step:
|