[MonoDevelop] Ideas for a new command system
Lluis Sanchez
lluis@ximian.com
Sat, 09 Apr 2005 02:22:03 +0200
Hi!
The next target in my quest for improving the architecture of MD is the
command system. There are two reasons why I don't like the current
design:
* It is not possible to change the behavior of a command depending
on the context in which it is run. For example, the Delete
command deletes the selected text in the editor, but we should
be able to use the same command for deleting the selected file
in the solution pad (if the pad has the focus), or whatever. Any
addin should be able to provide a custom behavior for an
existing command.
* It is not easy to handle the status of commands. Commands can be
enabled/disabled using conditions, but conditions must be
specified in the addin XML file and are very limited.
What I propose is a very different approach, so feedback is greatly
welcome (BTW, the main concept is taken from MS's MFC class library, not
a great library, but I like the approach it uses for command handling).
First of all, there is a global list of commands, which is independent
from where those commands are used. The command list would be
defined/extended in the addin xml like this:
<Extension path = "/SharpDevelop/Commands">
<Command id = "EditCommands.Copy"
_label = "Copy"
icon = "Icons.16x16.CopyIcon"
shortcut = "Control|C"/>
<Command id = "EditCommands.Paste"
_label = "_Paste"
icon = "Icons.16x16.PasteIcon"
shortcut = "Control|V"/>
<Command id = "EditCommands.Delete"
_label = "_Delete"
icon = "Icons.16x16.DeleteIcon"
shortcut = "Del"/>
</Extension>
Then, we can define menus and toolbars by refering those commands:
<Extension path =
"/SharpDevelop/Views/ProjectBrowser/ContextMenu/ProjectFileNode">
<MenuItem id = "Copy" command = "EditCommands.Copy"/>
<MenuItem id = "Paste" command = "EditCommands.Paste"/>
<MenuItem id = "Delete" command = "EditCommands.Delete"/>
</Extension>
The big change is how commands are executed. Instead of implementing a
single class for each command, we can implement command handler methods
in any class. For example, to handle the Delete command in the text
editor I would write a handler like this:
[CommandHandler (EditCommands.Delete)]
void OnDeleteCommand ()
{
// Delete the selection
}
If we want to implement the Delete command in a custom pad, we would add
a similar command handler in that pad. So, we have two handlers for the
same command in two different classes. Which one of them will be
executed when clicking on the Delete menu item? It depends on the
context.
When the command is clicked, the command manager looks for a command
handler by following a command route. This route begins at the widget
that has the focus and goes up in the parent chain. This means that if
we are editing some text in the editor, the delete command handler in
the editor will be executed. If we move the focus to the solution pad,
then the pad will get the commands.
The command manager will also automatically disable or hide commands for
which there isn't a handler in the active command route. This gives a
lot a consistency to the menu and toolbars, since options and buttons
will be grayed out when they can't be used (and there is no need to
write code for this, it comes for free).
If the status of a command depends on some internal logic, we can
implement that logic in some special command update handlers. For
example:
[CommandUpdateHandler (EditCommands.Delete)]
void OnUpdateDeleteCommand (CommandInfo commandInfo)
{
if (the_selected_tree_node_can_be_deleted()) {
commandInfo.Enabled = true;
commandInfo.Text = "Delete " + current_node.Name;
} else {
commandInfo.Enabled = false;
}
}
The command manager will look for a command update handler in the
current command route. If found, it will call the handler and will
update the menu items and buttons linked to that command accordingly. If
not found, it will disable them.
This is more or less the idea. There are many more details, such as
being able to customize the command route, defining global command
handlers for global commands, menu builders for the "Windows" menu list,
etc., but I just described the fundamental idea.
Comments?
Lluis.