Window Management
Volume Number: 10
Issue Number: 12
Column Tag: Improving the Framework
Related Info: Window Manager
Better Window Management in MacApp
Plugging some holes in MacApp windows
By Richard Gillam, GE Information Services
Note: Source code files accompanying article are located on MacTech CD-ROM or
source code disks.
The little things count. This article doesn’t aim to present any Higher Truths of
Object-Oriented Computing, or to show you how to solve third-order differential
equations using the THINK Class Library, or anything like that. Instead, I want to share
with you a little class I created a while back to plug up a few holes in MacApp 3.0.1’s
window-management code.
What’s Wrong With
MacApp’s Window Stuff?
Some things don’t quite live up to their press. As with printing in MacApp (“It’s
only a couple lines of code” only if you’re doing really basic, no-frills printing), so it
is with window management in MacApp, only not quite as bad. I found two significant
deficiencies in MacApp’s window-management code and one feature I wanted to add.
The first feature was MacApp’s window-staggering code. If you want the initial
positions of your document windows to march down the screen and to the right as you
successively open windows, rather than opening directly on top of each other, you can
set the window’s fMustStagger flag in the view template resource. But if you do that
without implementing a staggering routine of your own, you’ll run into two big
problems. The first is a truly brain-dead positioning algorithm. MacApp keeps a global
variable that contains a count of how many staggered windows it has opened. To figure
out where to open the next one, it just multiplies the standard staggering offset by the
number of windows that have already been opened. That’s it. This means that if you’ve
opened ten windows and then closed all of them, the next window you open will still be
halfway down the screen. Ugly.
Worse, there’s a bug in their algorithm. The algorithm is smart enough to sense
when you’ve marched off the edge of the monitor and move you back to the upper
left-hand corner, but they didn’t do it quite right. If you have two monitors and the
auxiliary monitor is placed to the right of the main monitor (the one with the menu
bar), you can run into a situation where new windows open with their initial position
straddling monitors, a definite human-interface no-no. This is either because they
don’t take multiple monitors into account or because they only check to see if the
window has gone off the bottom of the screen and not if it’s gone off the right-hand side.
[To see what I’m talking about, comment out the line that begins
“RegisterStdType(“TBetterWindow”)” in the demo program and compile and run it.
On a two-monitor machine, if you open enough windows, several of them will hang off
on the right-hand monitor. They won’t move back to the upper left-hand corner until
they march off the bottom.]
The other feature I wanted to improve was the window-zooming code. Here,
MacApp does take multiple monitors into account, but doesn’t take the contents of the
window itself into account. The human-interface guidelines say that clicking the zoom
box of a window should make it just large enough to hold its contents (if that’ll fit on
the screen), but no larger. MacApp’s default window-zooming algorithm always zooms
the window to full-screen size.
Finally, I needed to add the ability to save and restore window positions.
The TBetterWindow Class
You’ll find source for a class designed to solve that above problems and a demo
program called BetterWindow in the usual source code locations. The program is
basically the Nothing application beefed up with the addition of the TBetterWindow
class. I made the default view wider so that you could see the multiple-monitor bug,
and I added the ability to make the view itself larger and smaller. You may want to
follow along in the demo program as I go through these examples.
It would be nice if we could improve the positioning and zooming algorithms by
adding a behavior to a regular TWindow. Unfortunately, we can’t, because we have to
override too many routines which are specific to TWindow and not available to
override from TBehavior. So I had to subclass TWindow and create a class called
TBetterWindow. Fortunately, for certain classes, MacApp provides a way to use a
subclass instead of standard class. In your application’s initialization code (probably
in your application class’s I method), call RegisterStdType(). My call to
RegisterStdType() looks like this:
RegisterStdType("TBetterWindow", kStdWindow);
kStdWindow tells MacApp you’re providing a class name to use when opening a
standard window, and the string parameter is the class name to use instead of TWindow.
Now anywhere you use TWindow, TBetterWindow will be substituted.
You can probably safely use TBetterWindow all the time, even in cases (such as
dialog boxes) where you don’t need its features. But if you want more control over
when specifically you use TBetterWindow, you need to specify it in all of the view
hierarchy resources where you intend to use it.
Smart Window Positioning
To control how a window is staggered, you override TWindow::SimpleStagger().
TWindow::Open() calls this routine any time the window being opened has its
fMustStagger flag set. This routine positions and resizes the window as appropriate. It
takes two parameters: the amount to offset each window from the previous one, and a
count of the staggered windows opened so far. We don’t use the count parameter.
The basic idea behind this routine is that every new window opens offset by the
specified amount from the window right behind it. But we’ve added two important
refinements: 1) If that position will run partially off the screen or straddle two
monitors, we don’t use it, but instead move back toward the upper left-hand corner of
the monitor containing the largest part of the previous active window. 2) If the
position where we want to open the window is already occupied by another window
(i.e., another window has its upper left-hand corner where we were going to put this
window’s upper left-hand corner), then move down and to the right until we find an
unoccupied position.