[MonoDevelop] Ideas for a new command system

Lluis Sanchez lluis@ximian.com
Sun, 10 Apr 2005 13:05:53 +0200


On dg, 2005-04-10 at 00:04 -0300, Rafael Teixeira wrote:
> 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...

Yeah, that's the plan.

> 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
> > 
> 
>