@cha_commander
Commander was a library originally developed by Denis Kudriashov. Commander 2.0 is the second iteration of that library. It was designed and developed by Julien Delplanque and Stéphane Ducasse. Note that Commander 2.0 is not compatible with Commander but it is really easy to migrate from Commander to Commander 2.0. We describe Commander 2.0 in the context of Spec. From now on, when we mention Commander we refer to Commander 2.0. In addition, we show how to extend Commander to other needs.
Commander models application actions as first-class objects following the Command design pattern. With Commander, you can express commands and use them to generate menus and toolbars, but also to script applications from the command line.
Every action is implemented as a separate command class (subclass of CmCommand) with an execute method and the state required for execution.

We will show later that for a UI framework, we need more information such as an icon and shortcut description. In addition, we will present how commands can be decorated with extra functionality in an extensible way.
A command is a simple object instance of a subclass of the class CmCommand. It has a description, a name (this name can be either static or dynamic as we will show later on). In addition, it has a context from which it extracts information to execute itself. In its basic form, there is no more than that.
Let us have a look at examples. We will define some commands for the ContactBook application and illustrate how they can be turned into menus and a menubar.
For convenience reasons, we define a common superclass of all the commands of the contact book application named ContactBookCommand.
CmCommand << #ContactBookCommand
package: 'ContactBook'
We define a simple helper method to make the code more readable
ContactBookCommand >> contactBookPresenter
^ self context
For the same reason, we define another helper to access the contact book and the selected item.
ContactBookCommand >> contactBook
^ self contactBookPresenter contactBook
ContactBookCommand >> selectedContact
^ self contactBookPresenter selectedContact
Using the helper method isContactSelected we defined in the previous chapter, the method hasSelectedContact can be implemented as:
ContactBookCommand >> hasSelectedContact
^ self contactBookPresenter isContactSelected
We define a subclass to define the add a contact command.
ContactBookCommand << #AddContactCommand
package: 'ContactBook'
AddContactCommand >> initialize
super initialize.
self
basicName: 'New contact';
basicDescription: 'Creates a new contact and adds it to the contact book.'
AddContactCommand >> execute
| contact |
contact := self contactBookPresenter newContact.
self hasSelectedContact
ifTrue: [ self contactBook addContact: contact after: self selectedContact ]
ifFalse: [ self contactBook addContact: contact ].
self contactBookPresenter updateView
We define the method updateView to refresh the contents of the table.
ContactBookPresenter >> updateView
table items: contactBook contacts
Now in an inspector on an instance of ContactBookPresenter, we can simply execute the command as follows:
(AddContactCommand new context: self) execute
Executing the command should ask you to give a name and a phone number and the new contact will be added to the list.
We can also execute the following snippet.
| presenter command |
presenter := ContactBookPresenter on: ContactBook coworkers.
command := AddContactCommand new context: presenter.
command execute
Now we define now another command to remove a contact. This example is interesting because it does not involve any UI interaction. It shows that a command is not necessarily linked to UI interaction.
ContactBookCommand << #RemoveContactCommand
package: 'ContactBook'
RemoveContactCommand >> initialize
super initialize.
self
name: 'Remove';
description: 'Removes the selected contact from the contact book.'
This command definition illustrates how we can control when a command should or should not be executed. The method canBeExecuted allows specifying such a condition.
RemoveContactCommand >> canBeExecuted
^ self context isContactSelected
The method execute is straightforward.
RemoveContactCommand >> execute
self contactBook removeContact: self selectedContact.
self contactBookPresenter updateView
The following test validates the correct execution of the command.
ContactCommandTest >> testRemoveContact
self assert: presenter contactBook size equals: 3.
presenter table selectIndex: 1.
(RemoveContactCommand new context: presenter) execute.
self assert: presenter contactBook size equals: 2
Now that we have our commands, we would like to reuse them and turn them into menus. In Spec, commands that are transformed into menu items are structured into a tree of command instances. The class method buildCommandsGroupWith:forRoot: of SpPresenter is a hook to let presenters define the root of the command instance tree.
A command is transformed into a command for Spec using the message forSpec.We will show later that we can add UI-specific information to a command such as an icon and a shortcut.
The method buildCommandsGroupWith:forRoot: registers commands to which the presenter instance is passed as context. Note that here we just add plain commands, but we can also create groups. Later in this chapter we will also specify a menu bar in this method.
ContactBookPresenter class >>
buildCommandsGroupWith: presenter
forRoot: rootCommandGroup
rootCommandGroup
register: (AddContactCommand forSpec context: presenter);
register: (RemoveContactCommand forSpec context: presenter)
Now we have to attach the root of the command tree to the table. This is what we do with the new line in the initializePresenters method. Notice that we have full control and as we will show we could select a subpart of the tree (using the message /) and define it as root for a given component.
ContactBookPresenter >> initializePresenters
table := self newTable.
table
addColumn: (SpStringTableColumn title: 'Name' evaluated: #name);
addColumn: (SpStringTableColumn title: 'Phone' evaluated: #phone).
table actions: self rootCommandsGroup.
table items: contactBook contacts.
When reopening the interface with (ContactBookPresenter on: ContactBook coworkers) open, you should see the menu items as shown in Figure @withmenu@. As we will show later, we could even replace a menu item with another one, changing its name, or icon in place.

Commands can be managed in groups and such groups can be turned into corresponding menu item sections. The key hook method is the class method named buildCommandsGroupWith: presenterInstance forRoot:.
Here we give an example of such grouping. Note that the message asSpecGroup is sent to a group. We create two methods, each creating a simple group, one for adding, and one for removing contracts.
ContactBookPresenter class >> buildAddingGroupWith: presenter
^ (CmCommandGroup named: 'Adding') asSpecGroup
description: 'Commands related to contact addition.';
register: (AddContactCommand forSpec context: presenter);
beDisplayedAsGroup;
yourself
ContactBookPresenter class >> buildRemovingGroupWith: presenter
^ (CmCommandGroup named: 'Removing') asSpecGroup
description: 'Commands related to contact removal.';
register: (RemoveContactCommand forSpec context: presenter);
beDisplayedAsGroup;
yourself
We group the previously defined groups together under the contextual menu:
ContactBookPresenter class >> buildContextualMenuGroupWith: presenter
^ (CmCommandGroup named: 'Context Menu') asSpecGroup
register: (self buildAddingGroupWith: presenter);
register: (self buildRemovingGroupWith: presenter);
yourself
Finally, we revisit the hook buildCommandsGroupWith:forRoot: to register the last group to the root command group.
ContactBookPresenter class >>
buildCommandsGroupWith: presenter
forRoot: rootCommandGroup
rootCommandGroup
register: (self buildContextualMenuGroupWith: presenter)
When reopening the interface with (ContactBookPresenter on: ContactBook coworkers) open, you should see the menu items inside a 'Context Menu' as shown in Figure @withmenuContext@.

To show that we can also select part of the command tree, we select the 'Context Menu' group and declare it as the root of the table menu. Then you will not see the 'Context Menu' anymore.
ContactBookPresenter >> initializePresenters
table := self newTable.
table
addColumn: (SpStringTableColumn title: 'Name' evaluated: #name);
addColumn: (SpStringTableColumn title: 'Phone' evaluated: #phone).
table actions: self rootCommandsGroup / 'Context Menu'.
table items: contactBook contacts
Here we see that by sending the slash message (/), we can select the group in which we want to add a menu iten.
Building menus is nice, but sometimes we need to add a menu to an existing one. Commander supports this via a dedicated pragma, called <extensionCommands> that identifies extensions.
Imagine that we have new functionality that we want to add to the contact book and that this behavior is packaged in another package, here, ContactBook-Extensions.
First, we will define a new command and second, we will show how we can extend the existing menu to add a new menu item.
ContactBookCommand << #ChangePhoneCommand
package: 'ContactBook-Extensions'
ChangePhoneCommand >> initialize
super initialize.
self
name: 'Change phone';
description: 'Change the phone number of the contact.'
ChangePhoneCommand >> execute
self selectedContact phone: self contactBookPresenter newPhone.
self contactBookPresenter updateView
We extend ContactBookPresenter with the method newPhone to let the presenter decide how a user should provide a new phone number.
ContactBookPresenter >> newPhone
| phone |
phone := self
request: 'New phone for the contact'
initialAnswer: self selectedContact phone
title: 'Set new phone for contact'.
(phone matchesRegex: '\d\d\d\s\d\d\d')
ifFalse: [
SpInvalidUserInput signal: 'The phone number is not well formatted.
Should match "\d\d\d\s\d\d\d"' ].
^ phone
The last missing piece is the declaration of the extension. This is done using the pragma <extensionCommands> on the class side of the presenter class as follows:
ContactBookPresenter class >>
changePhoneCommandWith: presenter
forRootGroup: aRootCommandsGroup
<extensionCommands>
(aRootCommandsGroup / 'Context Menu')
register: (ChangePhoneCommand forSpec context: presenter)

By default a command does not know about Spec-specific behavior, because a command does not have to be linked to UI. Obviously you want to have icons and shortcut bindings when you are designing an interactive application.
Commander supports the addition of icons and shortcut keys to commands.Let us see how it works from a user perspective. The framework offers two methods to set an icon and a shortcut key: iconName: and shortcutKey:.
We should specialize the method asSpecCommand as follows:
RemoveContactCommand >> asSpecCommand
^ super asSpecCommand
iconName: #removeIcon;
shortcutKey: $x meta;
yourself
AddContactCommand >> asSpecCommand
^ super asSpecCommand
shortcutKey: $n meta;
iconName: #changeAdd;
yourself
Note that the commands are created using the message forSpec. This message takes care of the calling asSpecCommand.
At the time of writing this chapter, Commander management of shortcuts has not been pushed to Spec to avoid dependency on Commander. It is then the responsibility of your presenter to manage shortcuts as shown in the following method. We ask the command group to install the shortcut handler in the window.
ContactBookPresenter >> initializeWindow: aWindowPresenter
super initializeWindow: aWindowPresenter.
self rootCommandsGroup installShortcutsIn: aWindowPresenter
Commander supports the reuse and in-place customisation of commands. It means that a command can be modified on the spot: for example, its name or description can be adapted to the exact usage context. Here is an example that shows that we adapt the same command twice.
Let us define a really simple and generic command that will simply inspect the object.
ContactBookCommand << #InspectCommand
package: 'ContactBook-Extensions'
InspectCommand >> initialize
super initialize.
self
name: 'Inspect';
description: 'Inspect the context of this command.'
InspectCommand >> execute
self context inspect
By using a block, the context is computed at the moment the command is executed and the name and description can be adapted for its specific usage as shown in Figure @inPlaceCustomisation@.
ContactBookPresenter class >>
extraCommandsWith: presenter
forRootGroup: aRootCommandsGroup
<extensionCommands>
aRootCommandsGroup / 'Context Menu'
register:
((CmCommandGroup named: 'Extra') asSpecGroup
description: 'Extra commands to help during development.';
register:
((InspectCommand forSpec context: [ presenter selectedContact ])
name: 'Inspect contact';
description: 'Open an inspector on the selected contact.';
iconName: #smallFind;
yourself);
register:
((InspectCommand forSpec context: [ presenter contactBook ])
name: 'Inspect contact book';
description: 'Open an inspector on the contact book.';
yourself);
yourself)

Commander also supports menu bar creation. The logic is the same as for contextual menus: we define a group and register it under a given root, and we tell the presenter to use this group as a menubar.
Imagine that we have a new command to print the contact.
ContactBookCommand << #PrintContactCommand
package: 'ContactBook'
PrintContactCommand >> initialize
super initialize.
self
name: 'Print';
description: 'Print the contact book in Transcript.'
PrintContactCommand >> execute
Transcript open.
self contactBook contacts do: [ :contact | self traceCr: contact name , ' - ' , contact name ]
We create a simple group that we call 'MenuBar' (but it could be called anything).
ContactBookPresenter class >> buildMenuBarGroupWith: presenter
^ (CmCommandGroup named: 'MenuBar') asSpecGroup
register: (PrintContactCommand forSpec context: presenter);
yourself
We modify the root to add the menu bar group in addition to the previou one.
ContactBookPresenter class >>
buildCommandsGroupWith: presenter
forRoot: rootCommandGroup
rootCommandGroup
register: (self buildMenuBarGroupWith: presenter);
register: (self buildContextualMenuGroupWith: presenter)
We hook it into the widget as the last line of the initializePresenters method. Notice the use of the message asMenuBarPresenter and the addition of a new instance variable called menuBar.
ContactBookPresenter >> initializePresenters
table := self newTable.
table
addColumn: (SpStringTableColumn title: 'Name' evaluated: #name);
addColumn: (SpStringTableColumn title: 'Phone' evaluated: #phone).
table actions: self rootCommandsGroup / 'Context Menu'.
table items: contactBook contents.
menuBar := (self rootCommandsGroup / 'MenuBar') asMenuBarPresenter.
Finally, to get the menu bar we declare it in the layout. We use SpAbstractPresenter class>>#toolbarHeight to specify the height of the menu bar.
ContactBookPresenter >> defaultLayout
^ SpBoxLayout newVertical
add: #menuBar
withConstraints: [ :constraints | constraints height: self toolbarHeight ];
add: #table;
yourself

In this chapter, we saw how you can define a simple command and execute it in a given context. We show how you can turn a command into a menu item in Spec by sending the message forSpec. You learned how we can reuse and customize commands. We presented groups of commands as a way to structure menus and menu bars.