[comp.sys.mac.programmer] Windows menu and DrawMenuBar

tim@hoptoad.uucp (Tim Maroney) (08/18/89)

I'd like to have some discussion on the proper maintenance of a Windows
menu.  Like many other programs, mine do a complete menu item
enable/disable pass whenever the application detects a menu bar click.
Theoretically, it could be done at some other time more efficiently,
but the code would be harder to maintain.  It's better to have all the
related code grouped together in time and space.

So anyway.  Another thing I do at the same time is rebuild my Windows
menu.  This is a pretty simple piece of code which first truncates the
Windows menu down to a fixed header of generic window operations
("Stack", "Tile", "Next", "Previous"), then appends all the window
titles, iterating down the window list starting at FrontWindow().

The problem is that under System 6.0, it is neccessary to call
DrawMenuBar after adding any items to a menu.  If you don't, they will
not be visible to the user during MenuSelect.  DrawMenuBar causes the
menu bar to flicker, so now, every time the mouse is clicked in the
menu bar, there's a flicker before tracking starts.  I don't like this,
but updating the Windows menu this way is so easy I'm loathe to change
it.

Any suggestions?  Unless I get a better idea, I am going to try doing
the update of the windows menu when any activate event comes in
(including deactivate); if that fails, then I'm going to have to put in
a call to the update windows menu routine whenever a window is created
or destroyed, and I really do not want to do that.
-- 
Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

"I am convinced that cross-posting is an evil Satanic plot."
    -- Eugene Miya on soc.net-people, misc.headlines, misc.kids, misc.misc,
		      news.misc, and soc.misc

earleh@eleazar.dartmouth.edu (Earle R. Horton) (08/18/89)

In article <8320@hoptoad.uucp> tim@hoptoad.uucp (Tim Maroney) writes:
>I'd like to have some discussion on the proper maintenance of a Windows
>menu.  Like many other programs, mine do a complete menu item
>enable/disable pass whenever the application detects a menu bar click.
>Theoretically, it could be done at some other time more efficiently,
>but the code would be harder to maintain.  It's better to have all the
>related code grouped together in time and space.

     The problem with doing this at menu bar click time is that if you
have a large amount of menu housekeeping to do then you might slow
down your reponse time to the menu bar click.  If there is too much of
a delay before your menus drop down then your program will be
perceived as having performance problems.  And the worst offender is
probably the next subject you mention...

>So anyway.  Another thing I do at the same time is rebuild my Windows
>menu.  This is a pretty simple piece of code which first truncates the
>Windows menu down to a fixed header of generic window operations
>("Stack", "Tile", "Next", "Previous"), then appends all the window
>titles, iterating down the window list starting at FrontWindow().
>
>The problem is that under System 6.0, it is neccessary to call
>DrawMenuBar after adding any items to a menu.  If you don't, they will
>not be visible to the user during MenuSelect.

     I have not observed this behavior.  I am running 6.0.3 on a Mac
II and on a Plus, and find that I can add or delete menu items with
impunity without calling DrawMenuBar.  I do not get menu items which
are not visible.  I wish you would give more information about this
problem, since what you have written above makes me worry that I am
missing something.  I also dug out a 6.0 disk and tried it with the
same result:  No need to call DrawMenuBar.

     What I have observed, in my programs and apparently in other
people's programs, is that when you have one of these window menus you
can visibly slow down response time by having a large number of
windows open.  Rebuild that window menu every time, and you get a
perceptable wait between the time you click and the time you first see
a menu.

     I saw one solution to this problem at MacWorld Expo, at the
DayStar Digital* booth.

     For those of us who cannot afford this solution, one has to
resort to optimizing the software.  Rebuilding menus any time a change
occurs which can affect their appearance might be one way to improve
response to mouse clicks in the menu bar, but it has one serious
disadvantage that I can think of.  Many of the changes you make will
never be seen, since these will be overridden by later changes.  It is
also a bear to code this way if there are many things in the menus
which need changes, and many events which require them.

     Another solution is to maintain some kind of data structure which
reflects the validity of menus in your program that you determine by
testing to be performance offenders.  Reset all your validity flags
when you rebuild the menus, set them when events occur which call for
a change in menu appearance.

     Some percentage of the time, you will get lucky.  For instance,
it is certainly possible to have the user do things which will not
change the window ordering, and therefore will not invalidate your
window menu.  If you have a flag that you set every time something
happens to change the window list, then you will know whether to
rebuild the window menu before calling MenuSelect or not.

Earle R. Horton

*DayStar Digital makes a 50 MHz accelerator containing a 68030.  Eat
your heart out if you cannot afford one.  Chuckle at the rest of us if
you can.

nick@lfcs.ed.ac.uk (Nick Rothwell) (08/21/89)

In article <8320@hoptoad.uucp>, tim@hoptoad (Tim Maroney) writes:
>I'd like to have some discussion on the proper maintenance of a Windows
>menu.

>The problem is that under System 6.0, it is neccessary to call
>DrawMenuBar after adding any items to a menu.  If you don't, they will
>not be visible to the user during MenuSelect.

I think this comes from the beginning of time (viz. Inside Mac I),
rather than being anything new and System-6.0-ish...?

I use Paul duBois' TransSkel, and attach actions to ACTIVATE and
DEACTIVATE and so on. When a window activates or deactivates, it
alters its own menu item in the window menu, adding a tick or
whatever. A similar thing happens for creating and disposing of
windows.
   I essentially implement a lazy update. I have routines for (i)
putting in a windows menu, (ii) taking out a windows menu, and (iii)
updating the menu bar. There's a cache saying whether there's a window
menu or not (in fact, there's a menu ID here, because I have a number
of windows menus with different titles, depending on "mode", whoops,
pardon me, dirty word...). When a window deactivates, it removes the
menu but doesn't do an update. When a window appears, it adds a
windows menu, and re-draws the menu bar if the menu title is different
to the old one. It looks pretty smooth.

>(including deactivate); if that fails, then I'm going to have to put in
>a call to the update windows menu routine whenever a window is created
>or destroyed, and I really do not want to do that.

Any reason why not? I have everything driven from the
create/destroy/activate event handlers, and the structure doesn't seem
too bad.

>Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

		Nick.
--
Nick Rothwell,	Laboratory for Foundations of Computer Science, Edinburgh.
		nick@lfcs.ed.ac.uk    <Atlantic Ocean>!mcvax!ukc!lfcs!nick
~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~
               Fais que ton reve soit plus long que la nuit.

nick@lfcs.ed.ac.uk (Nick Rothwell) (08/21/89)

In article <193@castle.ed.ac.uk>, I write:
>In article <8320@hoptoad.uucp>, tim@hoptoad (Tim Maroney) writes:
>>The problem is that under System 6.0, it is neccessary to call
>>DrawMenuBar after adding any items to a menu.  If you don't, they will
>>not be visible to the user during MenuSelect.
>
>I think this comes from the beginning of time (viz. Inside Mac I),
>rather than being anything new and System-6.0-ish...?

Whoops, I misread Tim's original comment. You have to call DrawMenuBar
after an insert/delete menu, but I've never needed to call it after
inserting/deleting/changing a menu *item*. I think the problem must be
elsewhere...?

		Nick.
--
Nick Rothwell,	Laboratory for Foundations of Computer Science, Edinburgh.
		nick@lfcs.ed.ac.uk    <Atlantic Ocean>!mcvax!ukc!lfcs!nick
~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~
               Fais que ton reve soit plus long que la nuit.

dorner@pequod.cso.uiuc.edu (Steve Dorner) (08/21/89)

In article <8320@hoptoad.uucp> tim@hoptoad.uucp (Tim Maroney) writes:
>   ...  Like many other programs, mine do a complete menu item
>enable/disable pass whenever the application detects a menu bar click.
>Theoretically, it could be done at some other time more efficiently,
>but the code would be harder to maintain.  It's better to have all the
>related code grouped together in time and space.

I keep the related code together by having a routine called EnableMenus,
that I call whenever I need to, or every time the event loop runs.

It enables/disables menu items based on the program's current state, which
is mostly the kind and state of the topmost window (e.g., if the topmost
window is dirty, enable File:Save, etc.).  Also, the amount of state
information is small enough that I can save it each time through; if the
state hasn't changed since last time, I can skip the updating process.
Finally, there exists a flag in EnableMenus that can be set when I determine
that the menu bar needs to be redrawn.  The last thing EnableMenus does is
call DrawMenuBar if the flag is set.

This seemed to me to be a good compromise between efficiency and ease of
programming.  It's sort of ugly and inelegant, but that's just another way
of saying it was done on a Macintosh, isn't it? :-)
-- 
Steve Dorner, U of Illinois Computing Services Office
Internet: s-dorner@uiuc.edu  UUCP: {convex,uunet}!uiucuxc!dorner
IfUMust:  (217) 244-1765

tim@hoptoad.uucp (Tim Maroney) (08/24/89)

Thanks to those who've commented by mail and on the network on this
issue.  Most of the responses boiled down to a few points, so I will
try to address those points rather than respond to particular messages
in public.

(1)  The speed of menu updating.  Apparently there are a few programs
which take forever and a day to show their menus after you click in the
menu bar.  I have no idea what they are doing with all that time, but
it seems unlikely they're just consulting RAM data structures and
enabling/disabling menu items.  Even when I do build my Windows menu as
a response to the mouse click, there is no perceptible delay between
the click and the appearance of menus even on a 68000 machine.  Perhaps
the offending applications' data structures are built in such a way
that it takes a long time to access the information of the frontmost
window, but even then, I can't imagine it taking as long as a second.
My best guess is that they are alphabetizing something or touching the
disk.

EnableMenu and DisableMenu merely set a single bit at a fixed offset in
a menu data structure.  I really can't imagine that even a hundred of
them would take a humanly perceptible time.

(2)  Problems with when to fix the menu.  Again, I doubt that even with
ten windows open, there would be a perceptible delay building at the
click.  Unless, that is, you do something stupid like bubble sorting
the menu items in place.  However, just to be sure, I've moved to a
different approach.  There are only two places where I dispose of a
window or dialog, so I rebuild there; and since it is impossible to
create a window in front without generating an activate event, and it
is undesirable to create a window anywhere but in front, I rebuild when
I get an activate event (or a deactivate event) as well.  This seems to
go more than fast enough, particularly since I prefer to present
windows in window list order rather than alphabetically.

Why not do it everywhere I create a window or dialog?  I always create
them invisibly, then have the window-specific module show them when
they've been properly initialized.  There is no single place where
windows enter the list for that reason, though there is a single place
where they're created and have information bound to them.  Therefore,
this is just begging for me (or a developer using my code) to forget
the call to fix the windows menu and introduce a bug -- not a very
serious bug, but one that is not obvious and could easily find its way
into production code.

(3)  The need to call DrawMenuBar.  Those who have pointed out that
this is not necessary are correct.  I was misremembering a bug that
appeared last year when System 6.0 came out, bearing on long menus.
Suppose you delete a short menu and add a longer version of the same
menu.  The appearance of the menu bar doesn't change, so you'd think
the call to DrawMenuBar could be omitted (and so the flicker would be
avoided).  However, apparently System 6.0 caches the menu sizes or
something so that the long menu will be visible only up to the point
where the short menu ended.  I don't know why it didn't happen under
earlier systems.

So -- It is NOT necessary to call DrawMenuBar after adding to or
deleting from a menu.

(4)  Putting symbols into the windows menu.  I don't advise it.  One of
the things I like least about the Mac interface as practiced is the
reliance on cryptic symbols which actually convey far less than text.
FullWrite is a good example; the MPW Shell's use of checks and bullets
and so forth in its Windows menu is another.  Information which is not
asked for by the user should only be ionformation which is readily
comprehensible to the user.

If you must indicate things like who's in front, which windows have
changes, what was the last window you worked on, and so forth, don't
stick confusing symbols (or even worse, style changes) into the Windows
menu.  Instead, come up with a genuinely clear icon or small icon and
put that where it's needed.  Note: a triangle with a dot inside or some
such nonsense is not a clear icon.  Draw something real or don't draw
anything at all -- including a check mark or bullet!
-- 
Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

"Its failings notwithstanding, there is much to be said in favor
 of journalism in that by giving us the opinion of the uneducated, it
 keeps us in touch with the ignorance of the community." -- Oscar Wilde