Hey guys,
A few of you may remember that a few months ago, I attempted to create an Undo/Redo system inside of AppGameKit, that could be used in any kind of application that would require such functionality.
Although not perfect, this is the best technique I can come up with.
Basically, what I did is create a UDT called "DataBlock" which has fields which can be used to store any kind of variable, which the user could easily change out for any other.
Then, I created two global, infinite lists based on this UDT, so that both the undo list and redo list can share exactly the same type of data. When the user does a new action, say for example they place a tile in a map editor, the function "AddToUndoList" would be called, and a number would be passed in which would tell the computer "Ok, a tile was just placed, I need to remember what tile it was and where it's placed!". I do that with a Select - EndSelect statement, and a Case - EndCase statement.
Why don't I just paste the code here!
/* ============================================================*
* This API allows you to add undo and redo functionality *
* to any of your programs, such as a tile map/level editor *
* or any other program you would need such functionality for! *
* ============================================================*/
// First let's create a couple of constants which will keep our lists a reasonable size
#Constant UNDO_MAX_SIZE = 29 // this number is really 30 because lists start at 0!
#Constant REDO_MAX_SIZE = 29
// This UDT holds any data required and can be customized easily!
Type DataBlock
// This tells the computer what the action taken was
actionType as Integer
actionName as String
// For a tile map editor, use this to store tile properties
tileName as String
tileType as Integer
tilePosX as Integer
tilePosY as Integer
// Anything else you need can be added very easily!
EndType
// Create two expandable lists (NOTE: They *must* be global in order to access them properly!) on the DataBlock UDT
Global UndoList as DataBlock[]
Global RedoList as DataBlock[]
// Now create our functions which add actions to the undo and redo lists
Function AddToUndoList(actionType as Integer, actionName as String)
// First we'll check our list size. If it's long we'll delete the oldest element.
If UndoList.length > UNDO_MAX_SIZE Then UndoList.remove(0)
// Create a local variable also based on the DataBlock UDT
Local Undo as DataBlock
// Add the action type and name to the undo list
Undo.actionType = actionType
Undo.actionName = actionName
UndoList.insert(Undo)
EndFunction
Function AddToRedoList()
// First we'll check our list size. If it's long we'll delete the oldest element.
If RedoList.length > REDO_MAX_SIZE Then RedoList.remove(0)
// Create a local variable also based on the DataBlock UDT (same as before)
Local Redo as DataBlock
// Find the last action that was Undone to add to the RedoList
index = UndoList.length
// Add the action type and name to the redo list
Redo.actionType = UndoList[index].actionType
Redo.actionName = UndoList[index].actionName
RedoList.insert(Redo)
EndFunction
/* This function is called each time a new action is performed
* It checkes to see if the action being performed is happening while
* any elements exist in the RedoList. If they do, they MUST be deleted,
* so that we don't get errors later on! */
Function CheckNewAction()
// Check the length of the RedoList
If RedoList.length > -1 Then RedoList.length = -1
EndFunction
/* These functions are what actually undo/redo any previous action. */
Function Undo()
// First we must grab the very last action inserted into the UndoList
index = UndoList.length
lastAction = UndoList[index].actionType
// Then we will use a Select statement to determine what to undo!
Select lastAction
Case 1:
EndCase
Case 2:
EndCase
Case 3:
EndCase
EndSelect
EndFunction
Function Redo()
// First we must find the last action inserted into the RedoList
index = RedoList.length
lastAction = RedoList[index].actionType
// Determine what action to redo!
Select lastAction
Case 1:
EndCase
Case 2:
EndCase
Case 3:
EndCase
EndSelect
EndFunction
I'm wondering if you guys can maybe think of a better way? I mean sure, this works really well, but after a while, depending on just how functional your program is, you could literally end up with HUNDREDS of Case-EndCase statements to go through, and typing the numbers (it would be better to use clearly-defined #constants) would get quite tiring!
However, this is literally the only thing I can think of, so hopefully someone more brilliant than myself can come up with some suggestions!
Cheers!
Official Forum President from June 20th, 2015 - June 20th, 2016 (when my successor is elected, whomever that may be!).