"In the Land of Menus where the Items lie.
One Tag to rule them all, One Tag to define them,
One Tag to bring them all and in ubiquitousness bind them"
For some time now, I've been both both happy and annoyed by the VisualWorks use of tags (pragmas) to "extend" menus. I like it, because it works. It solves a problem. I don't like it because, it doesn't work, it doesn't solve all my problems.
I want to create a menu item which had dynamic hiding. As well as enablement. I also want a menu item which is a submenu, but which are dynamically hid/unhid. There are no menu tags that support hiding. Though MenuItem does. So it didn't solve my problem. I could hammer 2 more variant methods in, but this seems wrong.
The current set of menu tags is 13. Looking at them, one can see where it's headed. It's gonna be like the Dialog interface soon. Countless permutations of argument combinations. Sometimes able to find the one you want, sometimes close but not quite. Already many of them have the look of "passing a paragraph" (thanks to Steven Wessels for the term). Adding another tag is not as simple as adding yet another method either. Not only does the method have to be added to MenuAutomaticGenerator, but it has to be added to the Menu class's pragmas method. Which means an override. Overrides are great for "patches." Not for "pluggable APIs."
In truth what has happened with the Menu tags, is that in some sense we have made MenuItem private (not just it's special helper MenuItemAdornment). You create them through the declarative/template form provided in the tags. Doing this, you lose all the benefits of having a real object that you can send real messages to. Which means you've avoided using Smalltalks halmark strength. There's also the issue of keeping that template/declaration interface in sync with the MenuItem. Suppose you want to make a special subclass of MenuItem which does special things? How do you get one of those in your menu? Or we decide to add another attribute to MenuItem, then we have to futz with all of the template things? Or as in my case, you just want to use one of the already existing APIs (hidden:), but it's not exposed right now?
The solution I propose is to reduce the number of menu creation tags and use a more general one. The new tag is simply
<itemInMenu: anArrayOfMenuIDs position: aNumber>
That's it. Just two arguments. The tag identifies this method as one which will provide a menu item. Information which the menu item itself does not define (what menu it goes in, and where) is specified via the tag arguments. The rest of the behavior is configured by sending messages to the MenuItem object to be returned. Here's two examples I did with it:
<itemInMenu: #(#selectorMenu) position: 300.1>
^(MenuItem labeled: #SyncAssets >> 'Sync Assets' << #assets)
hidden: [self isAnyAssetClassSelected not];
enablement: #areAssetMethodsSelected;
value: #syncSelectedAssets
and
<itemInMenu: #(#selectorMenu) position: 300.2>
^(MenuItem labeled: #AddAssetsC >> 'Add Assets:' << #assets)
hidden: [self isSingleAssetClassSelected not];
submenu: [self computeAssetImportMenu]
These are really not much bigger/longer than the many argument tags I might have used to construct them. The difference is that I can pick and choose which APIs to use of MenuItem. Put them in what order I might. Use API's that aren't exposed. Use blocks. It's much more extensible and dynamic than what you can currently do, adding menu items to a menu via tags. I can take every other form of the menu tags and reduce it to one of these. The same cannot be said for them.
One difference, is that for the common action menu item (rather than submenu), I end up defining a method which adds the item, and another one which is actually the behavior of what to do. This even seems more correct though; separating the issue of "adding an item" vs "the action" Now I can use an itemInMenu: tag to simply invoke an existing method. Or define a menu item in a superclass, and have subclasses implement the action method differently.
This simple (3 method) change/extension can be had from the OpenRepository in the package named PragmaticMenuItems. In addition to making this possible, it also changes the way that Menu class pragmas is computed. Instead of being a long literal array which we have to override everytime we want a new tag (though why would we ever need any more after this), it simply uses another piece of Smalltalk reflection. It's implemented as:
pragmas
^MenuAutomaticGenerator organization listAtCategoryNamed: #generating
Hours later, I found I wasn't so original. John and Don (of refactory fame) used the exact same expression in the Smalllint stuff.
Philosiphical Aside: There is a "trap" that we seem to fall in repeatedly with these method tags. We try to program with them. Every experience I have like this convinces me that this is a bad idea. We should program with Smalltalk. We should use tags simply to annotate simple meta data to a given method.