REALbasic provides some support for MacOS X toolbars. The ToolbarItem and StandardToolbarItem classes allow you to add buttons to the toolbar, and certain standard window functionality is present. In particular, the toolbar button in the upper right corner of a window containing a toolbar allows the toolbar's visibility to be toggled. You can use the OS function ChangeWindowAttributes, discussed earlier, to hide that button. The attribute to modify is kWindowToolbarButtonAttribute = 64.
Other toolbar functionality can be added using MacOS functions; most such functions are found in the Window Manager and HIToolbar APIs. The ToolbarItem.Handle property returns an HIToolbarIteRef that you can pass to MacOS functions.
Here I cover some additional functionality that is not too difficult to add. It is possible to do a lot more with toolbars, including adding controls like an HISearchField. To do so requires the use of Carbon events and HIToolbox, and so is beyond the scope of this chapter. I do reach into later chapters here, but you should be able to use the code I reach for without additional effort.
Determining whether a window has a toolbar is simple. Given a window, the function GetWindowToolbar returns a reference to the toolbar, if one is attached to the window.
OSStatus GetWindowToolbar ( WindowRef inWindow, HIToolbarRef * outToolbar );
The HIToolbarRef type is declared in the header file HIToolbar.h. It is defined as an HIObjectRef, which is declared in the header file HIObject.h as a pointer to an opaque structure. Thus it is usually declared as Integer. Here the parameter outToolbar is a pointer to an HIToolbarRef, so we will declare the parameter by reference.
We wrap GetWindowToolbar into a WindowManager method.
Function HasToolbar(Extends w as Window) as Boolean #if targetCarbon If w Is Nil or w.MacWindowPtr = 0 then Return false End if Declare Function GetWindowToolbar Lib CarbonLib (inWindow as WindowPtr, ByRef outToolbar as Integer) as Integer dim theToolbar as Integer dim OSError as Integer = GetWindowToolbar(w, theToolbar) Return (theToolbar <> 0) #endif End Function
The WindowManager exposes functions that allow you to show or hide the toolbar.
Boolean IsWindowToolbarVisible ( WindowRef inWindow );
OSStatus ShowHideWindowToolbar ( WindowRef inWindow, Boolean inShow, Boolean inAnimate );
Passing false for the inAnimate parameter disables sliding.
Let us add methods to WindowManager to toggle the toolbar.
Sub ToolbarVisible(Extends w as Window, Assigns theValue as Boolean) #if targetCarbon Declare Function ShowHideWindowToolbar Lib CarbonLib (inWindow as WindowPtr, inShow as Boolean, inanimate as Boolean) as Integer dim OSError as Integer = ShowHideWindowToolbar(w, theValue, false) If OSError <> 0 then //handle error End if #endif End Sub
Function ToolbarVisible(Extends w as Window) as Boolean #if targetCarbon Declare Function IsWindowToolbarVisible Lib CarbonLib (inWindow as WindowPtr) as Integer Return IsWindowToolbarVisible(w) #endif End Function
The Apple Human Interface Guidelines suggest that applications have a View menu that includes a menu item for showing/hiding the toolbar. Here we write a ShowToolbarMenuItem subclass of MenuItem that provides this capability.
Because the toolbar functions are only available in OS 10.2+, we use soft declares. This also removes the need to wrap the code within #if targetCarbon...#endif blocks, and the need to write separate code for PEF and Mach-O builds. To our ShowToolbarMenuItem class we add a private constant, CarbonFramework = /System/Library/Frameworks/Carbon.framework.
First we implement the EnableMenu event handler.
Sub EnableMenu() me.KeyboardShortcut = "option-T" If NOT System.IsFunctionAvailable("IsWindowToolbarVisible", CarbonFramework) then me.Enabled = false Return End if Soft Declare Function IsWindowToolbarVisible Lib CarbonFramework (inWindow as WindowPtr) as Boolean If WindowCount > 0 then me.Enabled = me.HasToolbar(Window(0)) If IsWindowToolbarVisible(Window(0)) then ViewShowToolbar.Text = "Hide Toolbar" Else ViewShowToolbar.Text = "Show Toolbar" End if Else me.Enabled = false ViewShowToolbar.Text = "Show Toolbar" End if End Sub
A constructor would be the better place to set the KeyboardShortcut property. But if you add a ShowToolbarMenuItem in the IDE, then REALbasic will set KeyboardShortcut using the IDE settings after the object is constructed. To remove the need to set this every time in the IDE, we simply set the property in the EnableMenu event handler. Those you cannot bear such a waste of computing time can remove it and set up the menu item in the IDE.
The property HasToolbar used in the code above is actually a private ShowToolbarMenuItem function containing essentially the same code as the function HasToolbar implemented above. Perhaps you are thinking that duplicated code is a Bad Thing, and usually you would be correct to do so. Code duplication is bad for a couple of reasons. First, changes required by such code must be made in multiple places, increasing the opportunity for errors. Second, it increases the size of the executable.
But dependency is also a Bad Thing. It is reasonable to assume that the MacOS APIs are not going to change often, so once the code above is working, it will likely remain so. And duplicating a few lines of code may increase the size of the application, but so will the addition of the WindowManager module. Instead I opt here for a class that depends on no other code (other than its superclass, of course), allowing us to drop it into projects without having to drag other modules with it.
Next we implement the Action event handler.
Function Action() as Boolean If WindowCount = 0 then Return false End if If NOT System.IsFunctionAvailable("IsWindowToolbarVisible", CarbonFramework) then Return false End if If NOT System.IsFunctionAvailable("ShowHideWindowToolbar", CarbonFramework) then Return false End if Soft Declare Function IsWindowToolbarVisible Lib CarbonFramework (inWindow as WindowPtr) as Boolean Soft Declare Function ShowHideWindowToolbar Lib CarbonFramework (inWindow as WindowPtr, inShow as Boolean, inAnimate as Boolean) as Integer dim showToolbar as Boolean = NOT IsWindowToolbarVisible(Window(0)) dim OSError as Integer = ShowHideWindowToolbar(Window(0), showToolbar, true) If OSError <> 0 then Break End if Return true End Function
Setting ToolbarItem icons is a source of annoyance for me. The ToolbarItem.Image property allows you to set the icon using a REALbasic Picture, but the lack of support for masked images invariably requires me to hack away with Graphic Converter to get something that renders correctly.
Chapter 9, Icons, discusses how to work with MacOS icons from within REALbasic. Using the code there, we can easily set the icon of a ToolbarItem from any MacOS icon. Here, we will see here how to load an .icns file.
The HIToolbar function we need is HIToolbarItemSetIconRef.
OSStatus HIToolbarItemSetIconRef ( HIToolbarItemRef inItem, IconRef inIcon );
For the IconRef, we use two REALbasic classes developed in Chapter 9, MacIcon and a subclass, IcnsIcon. Given a .icns file f, you can create a MacIcon object as follows.
dim theIcon as MacIcon = new IcnsIcon(f)
MacIcon has a function Operator_Convert() as Integer that returns the IconRef that it wraps. This means that you can pass MacIcon objects to external functions that take an IconRef parameter, as in the code below.
Sub SetIcon(Extends theItem as ToolbarItem, theIcon as MacIcon) If theItem Is Nil then Return End if If NOT System.IsFunctionAvailable("HIToolbarItemSetIconRef", "Carbon.framework") then Return End if Soft Declare Function HIToolbarItemSetIconRef Lib "Carbon.framework" (inItem as Integer, inIcon as Integer) as Integer dim OSError as Integer = HIToolbarItemSetIconRef(me.Handle, theIcon) End Sub