Adding basic undo/redo to an AIR app using AS3
08.10.25
I was hoping that it wouldn't take too much code (and that it'd look cool since it is ActionScript), and I was pleasantly surprised:
private var _undo : Array; private var _redo : Array; private function verifyUndoRedo() : void { mainMenuData..menuitem.(attribute('data')=='undo').@enabled = _undo.length > 0; mainMenuData..menuitem.(attribute('data')=='redo').@enabled = _redo.length > 0; } public function addBasicUndoItem( s : Object, o : *, n : * ) : void { _redo = []; _undo.push( { undo:function():Object{ for( var p : * in o ){ s[p] = o[p]; }; return this; }, redo:function():Object{ for( var p : * in n ){ s[p] = n[p]; }; return this; } } ); verifyUndoRedo(); } private function undo() : void { if( _undo.length > 0 ){ _redo.push( _undo.pop().undo() ); } verifyUndoRedo(); } private function redo() : void { if( _redo.length > 0 ){ _undo.push( _redo.pop().redo() ); } verifyUndoRedo(); }
'Course you'll want to make sure your _undo and _redo Arrays are initialized to [] and that you're listening to the Ctrl-Z and Ctrl-Y KeyboardEvents...
public function onInvoke( invokeEvent : InvokeEvent ) : void { stage.addEventListener( KeyboardEvent.KEY_DOWN, handleKeyDown ); } private function handleKeyDown( e : KeyboardEvent ) : void { if( e.charCode == 122 && e.keyCode == 90 && e.ctrlKey ){ undo(); }else if( e.charCode == 121 && e.keyCode == 89 && e.ctrlKey ){ redo(); } }
... and you'll probably want to setup an easy to manage Menu...
<mx:XML format="e4x" id="mainMenuData" xmlns="" > <root> <menuitem label="File"> <menuitem label="Exit" data="exit" /> </menuitem> <menuitem label="Edit"> <menuitem label="Undo" data="undo" enabled="false" /> <menuitem label="Redo" data="redo" enabled="false" /> </menuitem> </root> </mx:XML> <mx:MenuBar width="100%" dataProvider="{mainMenuData}" labelField="@label" itemClick="handleMenuSelection(event)" showRoot="false" />
... with it's related handleMenuSelection method...
private function handleMenuSelection( e : MenuEvent ) : void { if( e.item.@data == "exit" ){ NativeApplication.nativeApplication.exit(); }else if( e.item.@data == "undo" ){ undo(); }else if( e.item.@data == "redo" ){ redo(); } }
... like I said, not too bad.
Using it isn't too taxing either. Assuming that you wanted to undo/redo the x and y position of an item after it had been dragged to a new position...
// Some code before the drag that set's the prevx and prevy var's to the previous x and y... addBasicUndoItem( this, {x:prevx,y:prevy}, {x:x,y:y} )