doMneu, alas

Richard Gaskin ambassador at fourthworld.com
Wed Mar 4 03:03:10 EST 2009


dunbarx wrote:

 > "doMenu", a command, is replaced by sending "menuPick", a message,
 > to a button, an object, renamed in this special case a "menu"?

Not necessarily.  You can also use "button" to refer to the object.  I 
believe the "menu" token was added mostly for HC compatibility, along 
with a few other things like "menuItem".

There's a subtle difference between "menu" and "button" when addressing 
menu buttons so they're not true synonyms, but in practice it seems 
"button" is more commonly used.


 > Well, OK, it works alright, but why not a simple command, oh, like,
 > um, "doMenu"? Instead we have a procedure.

As a general-purpose way of triggering menu items I also prefer 
HyperTalk's syntax to Transcript's, but the difference may be reflective 
of a very different philosophy of what the menu bar is all about, and 
why one would ever have a need to trigger a menu's script armed only 
with the item name and no knowledge of the scripts it calls: it becomes 
necessary when a menu is hard wired into the engine and has no script at 
all.


In HC the engine owned and controlled the menu bar, and allowed you to 
modify its contents to some degree but every time you booted HC it 
always started out with its own menus, requiring you to tear them down 
and rebuild them on the fly with scripts.

And even that only came later.  If memory serves, HC 1.0 provided little 
if any control over the menu bar other than limiting choices by changing 
the userLevel.

HC started out by defining its world as one in which the engine provided 
the environment and your stacks were more or less documents within that 
environment, almost like web pages in a browser.

As it grew, and as SuperCard, Plus, and others came along and pushed the 
xTalk envelope in new directions, HC expanded its vision to think of 
stacks more like applications, allowing full modification of the menu 
bar and eventually even deploying standalone applications.

In contrast, tools like SC and MC/Rev were built from the start as 
systems for deploying standalone applications.  As such, rather than 
providing a set of menus at runtime those engines have none at all, 
leaving the menu bar entirely up to the developer.

In a world where stacks are documents and the menu bar is provided by 
the engine, it makes sense to provide a way to trigger those commands. 
In fact, since HC menus had no scripts of their own, there was really no 
other way to do it but to send messages from outside the menu.

But in a world where the developer is responsible for the menu bar and 
everything in the menu bar has its own script, the approach used by SC 
and MC turns this around: rather than prodding menus from the outside, 
SC and MC work from the inside out, letting you call the shots from 
within the menu's own scripts.

Whether one way may seem better than another depends on a lot of things, 
not the least of which is one's own habits. :)

When I first signed on to the MetaCard list way back in the day I was a 
rantfest, screaming up one side and down the other about how "wrong" MC 
was in every way it differed from SC.

Over time I've come to appreciate the differences as largely borne of 
multi-platform considerations, as may also be at least a contributing 
factor with this:

 > If the formal syntax for the invoking of menu commands was written
 > down:
 >
 > send "menuPick" && "menuItem" to menu "menuName"
 >
 > then it would imply that Rev contains a native menu object, like HC.
 > You can only send messages to objects, right?

HC did not have menu objects, at least not anything like its buttons and 
fields.  HC menus had no scripts of their own, no true properties 
(though some clever syntax made menuItems almost feel like objects), and 
most of all no persistence: whatever you built in the menu bar during a 
session would disappear as soon as you quit, requiring that you rebuild 
it again from scratch on startup or openStack, often with rather long 
scripts.

SC is one of the very few xTalks that has true menu and menuItem objects.

Rev goes halfway between the two, implementing menus as buttons.  This 
gives you the speed of being able to replace an entire menu's contents 
in one move as with HC, but also the persistence and discrete script 
space SC offers.

The rationale Scott Raney gave me for using the button class is that a 
menu is in the generic sense a selector control, and so the differences 
between a checkbox and an option menu are conceptually minor; indeed 
even HyperCard implemented option controls, a form of popup menu, as 
buttons.

Remember that MC was born on Unix, where menus are part of the window. 
While I appreciate why Apple has a detached menu bar and believe it's 
measurably more efficient to use, I have to admit it's just not how the 
other 90% live.

So in an environment where menus are a part of the window, and inspired 
by HC which has option menus as buttons, it's just a short skip to 
extend the button class to handle all of the menu types, even pulldowns.

Sure, Mac folks often find this confusing at first.  I didn't much care 
for it myself when I first started working with MC.  But the more time I 
spent working on Windows, Linux, Irix and Solaris (ah, the good ol' 
days) the more I began to kinda like the flexibility that comes with 
using buttons for other types of menus beyond option controls.



All that said, I appreciate that it can be useful to have something like 
DoMenu, so below is a replacement handler I wrote for you that may help.

Because we can't have custom handlers overriding built-in commands (a 
small price to pay for an order-of-magnitude speed bump by streamlining 
the token table), this handler is named "MenuDo", which could also be 
capitalized as "menudo" which is regarded in my neighborhood as a cure 
for hangovers; maybe it'll help take the edge off of the nausea and 
headaches that come with the unlearning process as well. :)

Along with MenuDo is a function named MenuItemsToText, which isn't very 
interesting but is needed by MenuDo.

Use MenuDo just like DoMenu:

   MenuDo "Open Stack..."

It's kinda tossed together, but should work for most menus.

Put these handlers in your library, and hopefully they'll make a 
reasonable substitute for DoMenu as you continue to learn the wu wei of Rev.

Happy scripting -

--
  Richard Gaskin
  Fourth World
  Revolution training and consulting: http://www.fourthworld.com
  Webzine for Rev developers: http://www.revjournal.com

-----------------------

--
-- Call this instead of "DoMenu", using the label of any
-- menu item as the param in pItem
--
on MenuDo pItem
   put the menubar of this stack into tMbar
   if tMbar is empty then put the defaultMenuBar into tMbar
   if tMBar is empty then return "No menu bar active"
   --
   put empty into tMenuString
   repeat with i = 1 to the number of btns of tMBar
     put the long id of btn i of tMBar into tMenuObj
     put MenuItemsToText(the text of tMenuObj) into tMenuItems
     --
     put lineoffset(cr& pItem &cr, cr&tMenuItems &cr) into tOS
     if tOS > 1 then
       put line tOS of tMenuItems into tMenuString
       exit repeat
     end if
     if tMenuString is not empty then exit repeat
   end repeat
   --
   send "menuPick "&tMenuString to tMenuObj
end MenuDo


--
-- This function takes the contents of a menu object
-- and removes the metacharacter stuff to arrive at
-- a string which is hopefully like the one which
-- you'd get as a param to menuPick.  It's called
-- by MenuDo to reformat menu object contents into
-- a form which can be used to send a menuPick
-- message.  Needs some work, but handles most
-- basic cases okay:
--
function MenuItemsToText pItems
   put "!r,!u,!c,!n" into tEntities
   repeat for each item tEntity in tEntities
     replace cr&tEntity with cr in pItems
     replace tab&tEntity with tab in pItems
     replace tEntity&tab with tab in pItems
   end repeat
   replace "&" with empty in pItems
   put empty into tList
   set the itemdel to "/"
   put empty into tLastSubMemu
   repeat for each line tLine in pItems
     put item 1 of tLine into tMenuItem
     if char 1 of tMenuItem = tab then
       put tLastSubMenu &"|" into char 1 of tMenuItem
     else
       put tMenuItem into tLastSubMenu
     end if
     put tMenuItem &cr after tList
   end repeat
   --
   if char 1 of last item of tMenuItem = "(" then
     delete char 1 of last item of tMenuItem
   end if
   --
   return tList
end MenuItemsToText




More information about the Use-livecode mailing list