-
Notifications
You must be signed in to change notification settings - Fork 3
07 Advanced CommandHandlers
View this tutorial's changes on GitHub.
If you're working with the tutorial repo, open a Git Shell in the CoreEditor-HelloWorld-Example-as folder and run the following command:
git checkout -f step-7
Otherwise, follow the instructions below.
The previous tutorial didn't really add much to the Context that you didn't already learn earlier. However we are now going to write a CommandHandler that can access the 'dataProvider' property of your Context, and add new items to it.
Add the following code to your helloWorld.entities.Commands class:
public static const ADD_STRING:String = "helloWorld.addString";Now create the following class:
package helloWorld.commandHandlers
{
import core.appEx.core.commandHandlers.ICommandHandler;
import core.editor.CoreEditor;
import helloWorld.contexts.StringListContext;
public class AddStringCommandHandler implements ICommandHandler
{
public function AddStringCommandHandler()
{
}
public function execute(parameters:Object):void
{
var context:StringListContext = CoreEditor.contextManager.getLatestContextOfType(StringListContext);
var length:int = context.dataProvider.length;
context.dataProvider.addItem("Item " + (length+1));
}
}
}The only code we're really interested in here is the 3 lines within execute(). The first line requires some explanation, but essentially it is getting a reference to your StringListContext. The second two lines are simply adding a new item to its data provider.
The first line is using a manager on the CoreEditor API we haven’t seen yet. Contexts (and their associated View) can be opened and closed many times during a session, and receive and lose focus as the user interacts with different Views. The ContextManager is responsible for maintaining a list of all the Contexts currently active in the application, and also the order in which they were focused. It does this by monitoring focus events on IViewContainers. When a IViewContainer receives focus, it shifts its associated Context to the top of its internal list of Contexts.
It is important to understand why keeping track of the order of focus is important. You could have 2 Contexts that are both capable of having the UNDO Command performed on them. Knowing which one has most recently been focused allows the framework to determine which Context the user intuitively expects to have the Command carried out on.
So, coming back to this line:
var context:StringListContext = CoreEditor.contextManager.getLatestContextOfType(StringListContext);We are asking the ContextManager to return the most recently focused reference to a Context with the StringListContext type. Generally speaking, this is the only line of code you need to use in a CommandHandler to grab hold of the Context you are interested in.
Looking again at our CommandHandler, you may have spotted an assumption in the code. It assumes that the StringListContext has been opened, and is available to work with. In other words, we're not checking if the value returned by ContextManager.getLatestContextOfType() is null.
You can imagine that more complex CommandHandlers may make even more assumptions about the state of the application. For example, which items are selected on a Context (more on that later), or whether or not the user has a webcam.
You'd be surprised how much code you can end up writing that is simply checking to see if a certain set of conditions apply. Wouldn't it be nice if this niggling task could be abstracted out to some re-usable code…
The CoreApp framework contains a class of objects called Validators that among other things, can protect access to a CommandHandler's execute() function. The purpose of these is two-fold. Firstly, they eliminate the need to write loads of code like this:
if ( "the context I'm interested in isn't available" ) return;
if ( "the context I'm interested in isn't in the state I need" ) return;Secondly, they allow the framework to know beforehand whether a CommandHandler is able to be executed or not. This is useful information from a UI perspective, as we can use this information to enable/disable any associated Actions, so the user can clearly see which Actions are available at a given point in time.
So how do these Validators work, and how do we associate them with a CommandHandler? There is an appendix devoted entirely to Validators, and the various ways you can use them. In this tutorial, we will simply use one Validator to validate if a particular Context exists.
Add the following code to your extension’s constructor:
var commandHandlerFactory:CommandHandlerFactory = new CommandHandlerFactory( Commands.ADD_STRING, AddStringCommandHandler );
commandHandlerFactory.validators.push( new ContextValidator( CoreEditor.contextManager, StringListContext ) );
CoreApp.resourceManager.addResource( commandHandlerFactory );Rather than adding our CommandHandlerFactory to the ResourceManager all in one line, we're splitting things up here to make things clearer to read. Firstly we're creating the CommandHandlerFactory, and associating our new Command with our new CommandHandler.
The next line is the one we're really interested in. CommandHandlerFactories own an array of Validators. In this example we are pushing a single instance of a ContextValidator onto this array.
You may be wondering why we're passing the ContextValidator a reference to the ContextManager on the CoreEditor API. Why doesn't the ContextValidator just access the API itself? The reason is that Validators are a concept from the CoreApp Framework, not the CoreEditor Application. Validators have no knowledge of the static CoreEditor API, meaning they could be used in any application based on the CoreApp Framework.
Details of how Validators work and how the framework uses them to control access to your CommandHandler will be explained in future tutorials. For now we are going to focus on getting your CommandHandler up and running.
This is how we register a CommandHandler and its associated Validators with the framework. We can now be sure that the code in execute() function can only be called if there is an instance of StringListContext available.
Add the following code to your extension’s constructor:
CoreApp.resourceManager.addResource( new ActionFactory( StringListContext, Commands.ADD_STRING, "Add String", "myActions" ) );Build and run the application to see this new Action on your StringListView. Clicking it should add a new item to the end of the list.