Jump to content

How to create an undo/redo system?


Remi-X

Recommended Posts

local history = {} 
local h_Val = 0 
  
--When I change something 
history[#history+1] = what_it_is_now -- Get the current value 
h_Val = #history -- Store the current history time 
--Now change the thing that needs to be changed 
  
-- When I press undo 
setting = history[h_Val] -- Get the previous history item 
h_Val = h_Val-1 -- backward the history time 
  
-- When I press redo 
setting = history[h_Val+1] -- Get the next history item 
h_Val = h_Val+1 -- Forward the history time 
  

Is that correct, or is this way completely inefficient?

Edited by Guest
Link to comment

So, I should change this:

  
--When I change something 
history[#history+1] = what_it_is_now -- Get the current value 
h_Val = #history -- Store the current history time 
--Now change the thing that needs to be changed 
  

Into this:

  
--When I change something 
history[#h_Val+1] = what_it_is_now -- Get the current value 
h_Val = h_Val+1 
--Now change the thing that needs to be changed 
  

Or I'm getting wtf-ed..

Link to comment

Why is it really hard to make? Almost every simple program has an undo/redo button. And I just have to store small data.

Besides, after searching some articles using google, I haven't found something useful yet. It's all text, but no tutorials or examples.

Link to comment

Indeed, you question why its hard yet here you are lol...

There are a lot of programs out there with undo and redo that PARTIALLY work. Its rare to get something like Photoshop that can log and undo almost every single action on the GUI. Especially in any given order. You will often run into a situation where certain actions can be undone but they make it impossible to do previous undos before it. Example would be like converting a file to a different format. Then there are times you run into a situation where a popup comes and tells you something cannot be undone once done.

Some programs cheat... the cheating way? Keep logging EVERYTHING at a specific point in time for x steps. If the user has a problem retreat back to this state. Basically like a save state in an emulator. This is of course highly inefficient and not suitable for something like Photoshop. Maybe you could get away with it?

Link to comment

You can think of undo/redo system a bit like revision system. Of course you need to do a lot of extra work (you need to have a reverse method), so think if you really need it.

Also in MTA resource, why do you need it exactly? If it's something mutual for clients on serverside (like in the map editor?) then it adds another layer of complexity, which I would never want to deal with (programming).

However, this is a very good topic. I have thought on that myself too. I'm sure there are some nice articles out there, but discussing these things by self is also fun.

Link to comment

It's for my handlingeditor. It would be nice to revert changes since some values can give LSOD's. Those changes just contain numbers, bools, strings, or a simple small table. So, this should be stored:

  
oldValue["settingName"] = data    --string,bool,number 
--or 
oldValue["centerOfMass"] = { x, y, z }    --table for centerOfMass 
  

Nothing else, and my script can easily understand how to handle those different types of data. So that's not a problem.

Link to comment

I'll be an ass and not read your last post, as I just got ready with an example how I would implement a draft (and later refactor it into something more re-usable). I haven't coded in Lua for half a year, so there are probably errors, but it doesn't matter.

function setVehicleHeadLightColorWithUndo(vehicle, r, g, b) 
    --get for undo 
    local oldR, oldG, oldB = getVehicleHeadLightColor(vehicle); 
    --keep for redo 
    local action = setVehicleHeadLightColor(vehicle, r, g, b) 
    local undoAction = {setVehicleHeadLightColor(vehicle, oldR, oldG, oldB)} 
    addToActionsHistory(action, undoAction) 
end 
  
-- global undo table 
actionsHistory = {} 
  
-- ... in arguments list is a valid code in Lua, which means any extra arguments when method is called 
function addToActionsHistory(redoAction, undoAction)  
    local undone = false 
    local methodInfo = {redoAction, undoAction, undone} 
    table.insert(actionsHistory, methodInfo) 
end 
  
function markAsUndone(index) 
    local methodInfo = actionsHistory[index] 
    -- undone = true 
    methodInfo[3] = true 
    -- Can't remember if it's pointer or we need to overwrite the old table element with new data in Lua... 
end  
  
undo(steps) { 
    -- default value for steps 
    if steps==nil 
        steps=1 
    end 
    for iterator, methodInfo in ipairs(steps) do  
        local index = #actionsHistory-iterator-1         
        markAsUndone(index) 
        local methodInfo = actionsHistory[index] 
        local undoAction = methodInfo[2] 
        -- not sure if it was that easy in Lua 
        undoAction() 
    end 
} 
  
redo(steps) { 
    -- default value for steps 
    if steps==nil 
        steps=1 
    end 
    --TODO: Implement it 
    --check if undone from the end first... 
    --local redoAction = methodInfo[1] 
    --redoAction()   
} 

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...