Skip to content

Appendix B: CommandHandlers

Robert Silverton edited this page Jul 7, 2014 · 2 revisions

Overview

Now that we've seen how we can associate Validators with CommandHandlers to control when they are available, we can now explain a little more about the mechanism behind mapping a Command to a CommandHandler. Generally speaking, you don't need to know what's going on behind the scenes, but a little knowledge can help shed some light on things if you run into problems.

So far we've only ever mapped a single CommandHandler to a single Command. For most behaviour this will serve your purpose. But every so often you will want to implement behaviour for a Command that has a more generic quality to it.

For example, the CoreEditor application defines a number of Commands at core.editor.entities.Commands. Here is a selection of some of these:

public static const REFRESH			:String = "core.refresh";
public static const COPY			:String = "core.copy";
public static const CUT				:String = "core.cut";
public static const PASTE			:String = "core.paste";
public static const DELETE			:String = "core.delete";
public static const UNDO			:String = "core.undo";
public static const REDO			:String = "core.redo";

These are some reasonably generic commands. The CoreApp_Extensions create Actions for these, and bind some of them to keyboard shortcuts (such as CTRL+Z for UNDO). However you can imagine that implementing the behaviour of COPY would require a different approach if you were copying image data from an ImageEditorContext, compared to if you were copying text from a TextEditorContext.

This is a situation where you would have multiple CommandHandler's associated with the same Command. One CommandHandler would be responsible for copying image data, the other for copying text data. You would make sure that the CopyImageDataCommandHandler is associated with a ContextValidator that validates for the ImageEditorContext, and the CopyTextDataCommandHander is associated with a ContextValidator that validates for a TextEditorContext. Like so:

var commandHandlerFactory:CommandHandlerFactory = new CommandHandlerFactory( Commands.COPY, CopyImageDataCommandHandler );
commandHandlerFactory.validators.push( new ContextValidator( CoresEditor.contextManager, ImageEditorContext ) );
CoreApp.resourceManager.addResource( commandHandlerFactory );
commandHandlerFactory = new CommandHandlerFactory( Commands.COPY, CopyTextDataCommandHandler );
			commandHandlerFactory.validators.push( new ContextValidator( CoreEditor.contextManager, TextEditorContext ) );
CoreApp.resourceManager.addResource( commandHandlerFactory );

At run-time, if only an ImageEditorContext is available, then only the CopyImageDataCommandHandler will be validated. So no problems there. The same is true if only the TextEditorContext is available.

But what if both Contexts are available? This is a situation where the framework (specifically the CommandManager) helps simplify things, and removes the need to write any 'special case' code to handle this situation.

When both contexts are available, both of the Validators will have their state set to true. If the COPY Command is invoked, how does the framework choose which CommandHandler to execute? This is where the IMetricValidator interface comes in.

IMetricValidators

The IMetricValidator interface can be found at core.appEx.core.validators. Here it is:

package core.appEx.core.validators
{
	public interface IMetricValidator extends IValidator
	{
		function get metric():Number
	}
}

As you can see, there's not much to it. It extends IValidator and exposes a single property 'metric'. This is a number between 0-1 that reflects just how well 'validated' this particular Validator is. What is meant by this?

The ContextValidator class implements IMetricValidator and returns a number based upon how closely it matches the Context. It will return a higher metric if the Context it is matching has been focused more recently. The CommandManager can then use this metric to 'score' a CommandHandler based on specificity. It will then execute the CommandHandler with the best score.

The ContextValidator also takes into account the 'specificity' of its match.

Given the following two ContextValidators, which one is more closely matched if the current Context is an ImageEditorContext?

new ContextValidator( CoreEditor.contextManager, ImageEditorContext );
new ContextValidator( CoreEditor.contextManager, IVisualContext );

Hopefully you can intuitively see that both Validators would have a state of 'true' (as both Contexts implement IVisualContext), but the first Validator is definitely more 'specific'.

This is the mechanism by which the framework selects the most applicable CommandHandler.


< Previous | Next >

Clone this wiki locally