Menu Hide, Seek
Volume Number: 7
Issue Number: 2
Column Tag: Pascal Procedures
Related Info: Menu Manager Script Manager Window Manager
Menu Bar Hide & Seek
By D. Grant Leeper, Buena Park, CA
Note: Source code files accompanying article are located on MacTech CD-ROM or
source code disks.
[Grant has been programming the Mac since the original 128K model back in
1984. He is currently working on a 3D graphics library for MacApp and a set of
HyperCard XFCNs that allow HyperCard users to access the Script Manager 2.0 extended
date, geographic location, and number formatting facilities. He is also available for
contract programming work and can be contacted on CompuServe at 70157,1670 or on
AppleLink at D0428.]
The Disclaimer
The techniques described in this article should work on any current Macintosh
computer with the 128K ROMs or newer (512KE, Plus, SE series, II series) using
system 6.0.4 or less, with or without MultiFinder running. However several of the
steps described violate Apple’s rules for future compatibility and could break in the
future. Take this into account when implementing these techniques. These techniques
are not supported by Apple, if they break don’t blame Apple (or me), you were
warned!
Now, having said all that, on with the story.
The Introduction
So, in spite of all you’ve heard about future compatibility you have a case where
you just absolutely, positively have to hide the menu bar in your application. This is
the story of how you can do it.
The accompanying Pascal unit which I have called ShowHideMBar provides the
basic tools required to hide and redisplay the menu bar. When the menu bar is hidden
command key equivalents to items in the menu bar will still work. In addition, you can
detect a mouse click in the hidden menu bar and react to it by displaying the menu bar,
or just the menus, long enough to issue a single menu command.
I have also provided a sample application to demonstrate the use of this unit. It
is similar to sample applications provided by Apple’s own MacDTS and so it is rather
sparsely commented. For the most part the only comments in the code are those that
apply to hiding the menu bar. It is probably simpler for readers to apply the
techniques I describe to an existing application of their own rather than typing in the
entire example.
The Warning
A warning is appropriate here. Under MultiFinder, it is an extremely bad idea to
allow an application to become suspended or worse to quit while the menu bar is hidden.
If an application gets suspended without redisplaying the menu bar the new foreground
application will not have a menu bar visible. The user could end up trapped in an
application with no menu bar and no way out but to quit or reboot. The situation is
worse if an application exits without redisplaying the menu bar. The unit
ShowHideMBar saves the menu bar height as it existed before hiding the menu bar. It
uses this to restore the menu bar and desktop when the menu bar is to be made visible
again. If the application exits this information is lost.
Note that this warning does not apply to what Apple calls “minor switching” and
“update switching” (Tech Note #180, revised June 1989). Minor switching is when a
foreground application goes to sleep and background tasks receive null events to
perform background processing. Null events do not affect the menu bar so this is not a
problem. Update switching is when background applications get a chance to update their
windows. Since the disappearance of the menu bar can make previously hidden portions
of background windows visible as well as our own, we actually want this to happen.
Without MultiFinder the desktop is currently rebuilt when an application exits.
But since there’s no approved way for an application to tell if MultiFinder is running
it’s best to just assume it will be and code for it.
The sample application shows how to deal with this properly for a finished
application. It is important to be careful though when debugging an application because
the menu bar may not be visible when entering a debugger. For low level debuggers
like MacsBug this means you don’t want to issue an exit to shell or restart the
application command from the debugger if the menu bar is hidden. For high level
debuggers like SADE which use menu bars of their own you will not be able to access the
menu bar with the mouse if you enter it with the menu bar hidden.
The Tools
There are five public routines defined in this unit, they are declared as follows:
PROCEDURE HideMenuBar;
You call HideMenuBar when you want to make the menu bar invisible. You can
call it as soon as you have done the standard toolbox initializations if you want the menu
bar to be initially (or always) hidden. Or you can call it in response to a request from
the user. Calling it with the menu bar already hidden will have no effect.
PROCEDURE ShowMenuBar;
Call ShowMenuBar to display the menu bar if it is hidden, usually in response to
a request from the user, but also when suspending or quitting. Calling it with the menu
bar already showing will have no effect.
FUNCTION MenuBarVisible: BOOLEAN;
This function allows the application to determine if the menu bar is currently
visible.
FUNCTION PtInMenuBar(pt: Point): BOOLEAN;
Call this to find out if a point would be in the menu bar if it were visible. If
FindWindow returns inDesk and this function returns TRUE you could display the menu
bar long enough for the user to issue a single menu command.
FUNCTION HiddenMenuSelect(startPt: Point): LONGINT;
This routine is equivalent to the toolbox trap MenuSelect except that it can be
called when the menu bar is hidden. If in response to a mouse down event FindWindow
returns inDesk and PtInMenuBar returns TRUE, you can call this function to allow the
user to issue a menu command. It does not make the menu bar visible but only draws
individual menus as they are selected. If you want to display the entire menu bar at
this time call ShowMenuBar first then HiddenMenuSelect followed by HideMenuBar.
The Way It All Works
The Mac keeps track of the desktop and the menu bar by using low-memory
global variables. In order to hide the menu bar we have to modify some of these
variables (which are usually considered read only at best, this is why these techniques
are subject to breaking. Remember these techniques break some of the rules! Use
them cautiously.
The variables we are concerned with are MBarHeight and GrayRgn. MBarHeight
is the height, in pixels, of the menu bar at the top of the main screen. GrayRgn is a
handle to a region that defines the desktop. If there are multiple monitors in use on a
Mac the desktop will include them all. GrayRgn includes all of the normally gray area
on an empty desktop. It does not include the menu bar or the area outside the rounded
corners at the four corners of the desktop’s bounding box .
In order to hide the menu bar we first set the low-memory variable MBarHeight
to 0 while saving its existing value for when we want to make it visible again. Next we
add the area occupied by the menu to GrayRgn. Then we repaint the menu bar as part of
the desktop, generating update events for any windows that intersect the menu bar. And
finally, again for any windows that intersect the menu bar, we add the overlap with the
menu bar to their visRgn fields. The first two steps are easy, the last two are done for
us by calling the two low-level window manager routines PaintBehind and
CalcVisBehind which I’ll describe shortly.
In order to redisplay a hidden menu bar we follow a similar procedure. First we
restore the variable MBarHeight to its previous value. Then we remove the area
occupied by the menu bar from GrayRgn. Next we remove the area that overlaps the
menu bar from the visRgn fields of any windows that intersect the menu bar, again using
CalcVisBehind. And, finally we redraw the menu bar.
Accessing MBarHeight and GrayRgn is simple. On machines with the 128K or
larger ROMs the menu bar height is stored as a word at low-memory location $0BAA.