Building an Undoable Application


In this exercise, we'll update the previous sample application so that all the commands are undoable. This requires the following changes:

  • Edit each of the command classes so that they implement IUndoableCommand.

  • Edit the command button so that it adds executed commands to a command stack.

  • Add an undo button to the main class.

Making Undoable Commands

First, all the command classes must now implement IUndoableCommand. We'll start with RotateClockwiseCommand:

package com.peachpit.aas3wdp.commandexample.commands {    import com.peachpit.aas3wdp.commands.IUndoableCommand;    import flash.display.DisplayObject;    public class RotateClockwiseCommand implements IUndoableCommand{       private var _receiver:DisplayObject;       public function RotateClockwiseCommand(receiver:DisplayObject) {          _receiver = receiver;       }       public function execute():void {          _receiver.rotation += 20;       }              public function undo():void {          _receiver.rotation -= 20;       }    } }


Next we'll make a similar edit to RotateCounterclockwiseCommand:

package com.peachpit.aas3wdp.commandexample.commands {    import com.peachpit.aas3wdp.commands.IUndoableCommand    import flash.display.DisplayObject;    public class RotateCounterclockwiseCommand implements IUndoableCommand {       private var _receiver:DisplayObject;       public function RotateCounterclockwiseCommand(receiver:DisplayObject) {          _receiver = receiver;       }       public function execute():void {          _receiver.rotation -= 20;       }            public function undo():void {          _receiver.rotation += 20;       }    } }


Likewise we'll edit ScaleUpCommand:

package com.peachpit.aas3wdp.commandexample.commands {    import com.peachpit.aas3wdp.commands.IUndoableCommand;    import flash.display.DisplayObject;    public class ScaleUpCommand implements IUndoableCommand {       private var _receiver:DisplayObject;       public function ScaleUpCommand(receiver:DisplayObject) {          _receiver = receiver;       }       public function execute():void {          _receiver.scaleX += .1;          _receiver.scaleY += .1;       }       public function undo():void {          _receiver.scaleX -= .1;          _receiver.scaleY -= .1;       }    } }


And then we'll edit ScaleDownCommand:

package com.peachpit.aas3wdp.commandexample.commands {    import com.peachpit.aas3wdp.commands.IUndoableCommand;    import flash.display.DisplayObject;    public class ScaleDownCommand implements IUndoableCommand {       private var _receiver:DisplayObject;       public function ScaleDownCommand(receiver:DisplayObject) {         _receiver = receiver;       }       public function execute():void {          _receiver.scaleX -= .1;          _receiver.scaleY -= .1;       }       public function undo():void {          _receiver.scaleX += .1;          _receiver.scaleY += .1;       }    } }


Each of the changes in the command classes amounts to the same thing: implement IUndoableCommand rather than ICommand, and add the undo() method so that it reverses the effect of the execute() method.

Recording Command History

We can next modify the command button so that it records the command history. We'll accomplish this by using CommandStack. Each time the command button calls the execute() method of a command object, it will also add the command object to the stack.

package com.peachpit.aas3wdp.commandexample.controls {    import com.peachpit.aas3wdp.controls.BasicButton;    import com.peachpit.aas3wdp.commands.ICommand;    import flash.events.MouseEvent;    import com.peachpit.aas3wdp.commands.CommandStack;    public class CommandButton extends BasicButton {       private var _command:ICommand;       public function set command(value:ICommand):void {          _command = value;       }       public function CommandButton(label:String) {          super(label);          addEventListener(MouseEvent.CLICK, onClick);       }       private function onClick(event:MouseEvent):void {            if(_command != null) {             _command.execute();             CommandStack.getInstance().putCommand(_command);          }       }    } }


With this change, we now have a history of the commands that have been executed.

Adding an Undo Button

Next we add an undo button to the main class. The undo button-click event handler retrieves the last-run command. It then tests to see whether it is an undoable command. If so, it calls undo().

package {    import flash.display.Sprite;    import com.peachpit.aas3wdp.commandexample.commandcontainers.CommandContainer;    import com.peachpit.aas3wdp.commandexample.shapes.Rectangle;    import com.peachpit.aas3wdp.commandexample.commands.RotateClockwiseCommand;    import com.peachpit.aas3wdp.commandexample.commands.RotateCounterclockwiseCommand    import com.peachpit.aas3wdp.commandexample.commands.ScaleUpCommand;    import com.peachpit.aas3wdp.commandexample.controls.CommandButton;    import com.peachpit.aas3wdp.controls.BasicButton;    import com.peachpit.aas3wdp.commandexample.commands.ScaleDownCommand;    import flash.events.MouseEvent;    import com.peachpit.aas3wdp.commands.CommandStack;    import com.peachpit.aas3wdp.commands.ICommand;    import com.peachpit.aas3wdp.commands.IUndoableCommand;    public class CommandExample extends Sprite {           public function CommandExample() {          var rectangle:Rectangle = new Rectangle(0xFFFFFF, 50);          rectangle.x = 200;          rectangle.y = 200;          addChild(rectangle);                    var button:CommandButton = new CommandButton("apply command");          addChild(button);          button.y = 250;          var container:CommandContainer = new CommandContainer(new             RotateClockwiseCommand(rectangle), "rotate clockwise", 0, 0);          addChild(container);          container = new CommandContainer(new RotateCounterclockwiseCommand             (rectangle), "rotate counter-clockwise", 0, 55);          addChild(container);          container = new CommandContainer(new ScaleUpCommand(rectangle), "scale up",             0, 110);          addChild(container);          container = new CommandContainer(new ScaleDownCommand(rectangle),              "scale down", 0, 165);          addChild(container);          var undoButton:BasicButton = new BasicButton("undo");          addChild(undoButton);          undoButton.y = 280;          undoButton.addEventListener(MouseEvent.CLICK, onUndo);       }       private function onUndo(event:MouseEvent):void {          var stack:CommandStack = CommandStack.getInstance();          if(stack.hasPreviousCommands()) {             var command:ICommand = stack.previous();             if(command is IUndoableCommand) {                IUndoableCommand(command).undo();             }          }        }    } }


With this revision, you can now test the application. As you run commands, you can also undo them by clicking the undo button.




Advanced ActionScript 3 with Design Patterns
Advanced ActionScript 3 with Design Patterns
ISBN: 0321426568
EAN: 2147483647
Year: 2004
Pages: 132

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net