[MonoDevelop] Ideas for a new command system

Rafael Teixeira Rafael Teixeira <monoman@gmail.com>
Sun, 10 Apr 2005 00:04:24 -0300

Looks very nice. Just performance-wise I would be carefull to
transform the attributes metadata into some indexed form when first
scanning for command handlers to avoid repetitive reflection costs...
May be a premature optimization, but I can't help thinking it will
effectively be needed to attain adequate UI responsiveness.

Have fun, 

On Apr 8, 2005 9:22 PM, Lluis Sanchez <lluis@ximian.com> wrote:
> 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.
> _______________________________________________
> Monodevelop-list mailing list
> Monodevelop-list@lists.ximian.com
> http://lists.ximian.com/mailman/listinfo/monodevelop-list

Rafael "Monoman" Teixeira
I'm trying to become a "Rosh Gadol" before my own eyes. 
See http://www.joelonsoftware.com/items/2004/12/06.html for enlightment.
It hurts!