To create a toolbar, Delphi includes a specific component that encapsulates the corresponding Win32 common control or the corresponding Qt widget in VisualCLX. This component provides a toolbar with its own buttons, and it has many advanced capabilities. You place the component on a form and then use the component editor (the shortcut menu activated by a right-click) to create
The toolbar is
The tbsButton style indicates a standard pushbutton.
The tbsCheck style indicates a button with the behavior of a check box, or that of a radio button if the button is grouped with the others in its block (determined by the presence of separators).
The tbsDropDown style indicates a drop-down button (a
The tbsSeparator and tbsDivider styles
To create a graphic toolbar, you can add an ImageList component to the form, load some bitmaps into it, and then connect the ImageList with the
Images
property of the toolbar. By default, the images will be assigned to the buttons in the order they appear, but you can change this behavior quite easily by setting the
ImageIndex
property of each toolbar button. You can prepare further ImageLists for special button conditions and assign them to the
DisabledImages
and
HotImages
properties of the toolbar. The first
| Note |
In a nontrivial application, you will
|
As an example of the use of a toolbar, I've built the RichBar application, which has a RichEdit component you can
I won't cover the many details of the RichEdit control's features, which I
Figure 6.5:
The RichBar example's toolbar. Notice the drop-down menu.
The various buttons implement features, including opening and saving text files—the program asks the
Besides file operations, the program supports copy and paste operations and font management. The copy and paste operations don't require an interaction with the VCL Clipboard object, because the component can handle them with simple commands like these:
RichEdit.CutToClipboard; RichEdit.CopyToClipboard; RichEdit.PasteFromClipboard; RichEdit.Undo;
It is a little more advanced to know when these operations (and the corresponding buttons) should be enabled. You can enable Copy and Cut buttons when some text is selected, in the OnSelectionChange event of the RichEdit control:
procedure TFormRichNote.RichEditSelectionChange(Sender: TObject); begin tbtnCut.Enabled := RichEdit.SelLength > 0; tbtnCopy.Enabled := tbtnCut.Enabled; end;
The Copy operation cannot be determined by an action of the user, because it depends on the content of the Clipboard, which is also influenced by other applications. One approach is to use a timer and check the Clipboard content from time to time. A better approach is to use the OnIdle event of the Application object (or the ApplicationEvents component). Because the RichEdit control supports multiple Clipboard formats, the code cannot simply look at those, but should ask the component itself, using a low-level feature not surfaced by the Delphi control:
procedure TFormRichNote.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean); begin // update toolbar buttons tbtnPaste.Enabled := RichEdit.Perform (em_CanPaste, 0, 0) <> 0; end;
Basic font management is given by the Bold and Italic buttons, which have similar code. The Bold button toggles the relative attribute from the selected text (or changes the style at the current edit position):
procedure TFormRichNote.BoldExecute(Sender: TObject); begin with RichEdit.SelAttributes do if fsBold in Style then Style := Style - [fsBold] else Style := Style + [fsBold]; end;
Again, the current status of the button is determined by the current selection, so you'll need to add the following line to the RichEditSelectionChange method:
tbtnBold.Down := fsBold in RichEdit.SelAttributes.Style;
Besides a series of buttons, the RichBar example has a drop-down menu and a couple of combo boxes, a feature shared by many common applications. The drop-down button allows selection of the font
The Size button is connected to a PopupMenu component (called SizeMenu ) using the DropdownMenu property. A user can click the button, firing its OnClick event as usual, or select the drop-down arrow, open the pop-up menu (see again Figure 6.5), and choose one of its options. This case has three possible font sizes, per the menu definition:
object SizeMenu: TPopupMenu object Small1: TMenuItem Tag = 10 Caption = 'Small' OnClick = SetFontSize end object Medium1: TMenuItem Tag = 16 Caption = 'Medium' OnClick = SetFontSize end object Large1: TMenuItem Tag = 32 Caption = 'Large' OnClick = SetFontSize end end
Each menu item has a tag indicating the actual size of the font, activated by a shared event
handler:
procedure TFormRichNote.SetFontSize(Sender: TObject); begin RichEdit.SelAttributes.Size := (Sender as TMenuItem).Tag; end;
The ToolBar control is a
ComboFont.Items := Screen.Fonts; ComboFont.ItemIndex := ComboFont.Items.IndexOf (RichEdit.Font.Name )
The combo box initially displays the name of the default font used in the RichEdit control, which is set at design time. This value is recomputed each time the current selection changes, using the font of the selected text, along with the current color for the ColorBox:
procedure TFormRichNote.RichEditSelectionChange(Sender: TObject); begin ComboFont.ItemIndex := ComboFont.Items.IndexOf (RichEdit.SelAttributes.Name); ColorBox1.Selected := RichEdit.SelAttributes.Color; end;
When a new font is selected from the combo box, the reverse action takes place. The text of the current combo box item is assigned as the name of the font for any text selected in the RichEdit control:
RichEdit.SelAttributes.Name := ComboFont.Text;
The selection of a color in the ColorBox activates similar code.
Building a status bar is even simpler than building a toolbar. Delphi includes a specific StatusBar component, based on the corresponding Windows common control (a similar control is available in VisualCLX). This component can be used almost like a panel when its SimplePanel property is set to True. In this case, you can use the SimpleText property to output some text. The real advantage of this component, however, is that it allows you to define a number of subpanels by activating its Panels property editor. (You can also display this property editor by double-clicking the status bar control or perform the same operations using the Object TreeView.) Each subpanel has its own graphical attributes, which you can customize using the Object Inspector. Another feature of the status bar component is the "size grip" area added to the lower-right corner of the bar, which is useful for resizing the form. This is a typical element of the Windows user interface, and you can partially control it with the SizeGrip property (it auto-disables when the form is not resizable).
A status bar has various uses. The most common is to display information about the menu item currently selected by the user. In addition, a status bar often displays other information about the status of a program: the position of the cursor in a graphical application, the current line of text in a word processor, the status of the lock keys, the time and date, and so on. To show information on a panel, you use its Text property, generally in an expression like this:
StatusBar1.Panels[1].Text := 'message';
In the RichBar example, I've built a status bar with three panels, for command hints, the status of the Caps Lock key, and the current editing position. The StatusBar component of the example actually has four panels—you need to define the fourth in order to
| Tip |
Again, for more detail about the RichBar program, see the RTF file in the example's source code. Notice also that because the hints are to be displayed in the first panel of the status bar, I could have simplified the code by using the AutoHint property. I preferred showing the more detailed code, so you'll be able to customize it. |
The panels are not independent components, so you cannot access them by name, only by position as in the
const sbpMessage = 0; sbpCaps = 1; sbpPosition = 2;
In the first panel of the status bar, I want to display the toolbar button's hint message. The program obtains this effect by handling the application's OnHint event, again using the ApplicationEvents component, and copying the current value of the application's Hint property to the status bar:
procedure TFormRichNote.ApplicationEvents1Hint (Sender: TObject); begin StatusBar1.Panels[sbpMessage].Text := Application.Hint; end;
By default, this code displays in the status bar the same text of the fly-by hints, which aren't generated for menu items. You can use the Hint property to specify different strings for the two cases, by writing a string divided into two portions by a separator: the pipe ( ) character. For example, you might enter the following as the value of the Hint property:
'NewCreate a new document'
The first portion of the string, New , is used by fly-by hints, and the second portion, Create a new document , by the status bar. You can see an example in Figure 6.6.
Figure 6.6:
The StatusBar of the RichBar example displays a more detailed description than the fly-by hint.
| Tip |
When the hint for a control is made up of two strings, you can use the
GetShortHint
and
GetLongHint
|
The second panel displays the status of the Caps Lock key, obtained by calling the
GetKeyState
API function, which returns a state number. If the low-order bit of this number is set (that is, if the number is odd), then the key is pressed. I've decided to check this state when the application is idle, so the test is executed every time a key is pressed but also as soon as a message
procedure TFormRichNote.CheckCapslock; begin if Odd (GetKeyState (VK_CAPITAL)) then StatusBar1.Panels[sbpCaps].Text := 'CAPS' else StatusBar1.Panels[sbpCaps].Text := ''; end;
Finally, the program uses the third panel to display the current cursor position (measured in lines and
procedure TFormRichNote.RichEditSelectionChange(Sender: TObject); begin ... // update the position in the status bar StatusBar.Panels[sbpPosition].Text := Format ( '%d/%d' , [RichEdit.CaretPos.Y + 1, RichEdit.CaretPos.X + 1]); end;