Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Current Undo Action Limitation #46

Open
christianbrugger opened this issue Jan 1, 2015 · 5 comments
Open

Current Undo Action Limitation #46

christianbrugger opened this issue Jan 1, 2015 · 5 comments

Comments

@christianbrugger
Copy link
Member

In the current version b484ff2 undo is implemented on GUI side. Whenever the user insert, deletes or changes items new undo commands are created. However, there are no undo commands created on updates from the backend side.

This currently puts the backend under the restriction that it may not interfere with updates, e.g. create or delete objects on its own on the first level or change position, #inputs of existing objects. Otherwise the history will corrupt.

[Since there is no undo for created objects, they may collide with items that may be moved under it with an undo command created before the insertion]

I see two alternative solutions here:

  • Alternative A: Move the undo stack into the backend. We would push the actions to the backend (e.g. as changes in metadata instead of lambdas). Then the backend would handle the complete history, also adding undo commands from internal changes. Also this seems to be the best option for a multi-user environment, if we ever want to go there, since both see the same stack.
  • Alternative B: Also create undo actions on updates from the backend on GUI side, to keep the history consistent. This has the advantage, that the current infrastructure can stay. However, currently the GUI has not enough information to do so. It is related to the issue, that updates are reported back:
    [Let's say the user moved an object from X to Y, which is nicely reported to the backend. The backend reports back the change and an undo action is created, that is all fine. The problem starts, when the user executes the undo action. The undo action executes and moves object from Y back to X. It also reports the backend that the object is now on X. The back reports, however a while later and that is the problem here, that the object is now at position X. Now the GUI has no other option to create another undo action, because it doesn't know whether this change came from him or the backend. (Imagine the situation the backend rejected the change.) Creating an undo action at this point destroys the redo action (since there is only a linear history of changes). This makes the whole undo system pointless, as the user can never go back to the original state, where the object was at position Y.]
@hacst
Copy link
Member

hacst commented Jan 6, 2015

  • I guess B is viable using batch updates with basic pre-conditions and some backend internal guarantees to keep track on the last undo-able event might work assuming a low contention environment if we want to keep as much responsibility for this in the frontend as possible. You'd basically tell it: Hey, here's a batch of commands and my assumption for them is that the last "significant" state update you had is "X". With my recent changes each command executed in a batch is marked with a batch-id and every command you send now has a request-id with resulting updates tagged as "in-reply-to" it.
  • With A you'd save the frontend(s) a lot of trouble but force the backend to gain a notion of what kinda of meta-data changes are undoable and when they stop redoable. For example we definitely don't want an input state changes to be undoable. This forces us to make some additional assumptions in the backend though nothing to horrible I guess. Likely we would want to leave most of them to the Elements. Contention is ofc also a problem here but using the right API and contracts with the frontend shouldn't be simple enough to work with.

For global undo/redo I'm for option A too. If we have stateless(ish) frontends (aka browser) or multi-user it can support re-connects without loosing undo/redo history and it also allows us to persist it with the rest of the simulation state if we want to.

However from a users POV I think a global undo/redo might be a bit questionable. I personally would expect to only see my actions in my history. If mine get mixed with another users it would break my workflow as I can no longer work independently even if I'm in a completely unrelated part of the circuit. Undo/redo would become unusable.

I'm not sure that the backend can - without breaking the abstractions we have - keep track of what changes are incompatible (e.g. overlap etc.) which would preclude option A from being used for per-user undo/redo.

Definitely not an easy problem to solve.

@christianbrugger
Copy link
Member Author

Thanks for sharing your thoughts on this topic.

I also agree that option A makes little sense from a users POV, as you have nicely pointed out. For a stateless environment, I think there the same argument holds true. Furthermore, I think loosing connection is rare and we might decide that this result in an undo&redo loss, which I think is not too critical.

Before we decide on a solution, I think we should think about multi-user environments in general. And whether we allow editing at the same time. Currently the system does not facilitate that (image two users moving object to same position). One solution that comes to my mind is inspired on what git is doing. The idea that if you want to commit anything, you have to proof to the backend, that you have applied all the changes. [So when user B moves an object and user A at the same time, but B commits first. A cannot commit the change, before it applied the update from B. In that process it would recognize that the new position is invalid and cancels the operation.] For that we simply have to generate a version number for backend states.

@hacst
Copy link
Member

hacst commented Jan 8, 2015

Yeah. I think loosing undo/redo when restarting/loading the application is acceptable (pretty common behavior so the user won't be surprised).

I think editing at the same time is something we can support. Same as collaborative editing of a document or working with revision control this will only work well when not trying to edit the same thing at the same time which should be true for the majority of user operations. For collisions we just have to make sure we stay in sync and behave consistently as there's no real way to usefully "merge" user operations.

What you suggest in terms of assuring state assumptions made by the frontend hold before applying the changes matches what I tried to express in my first point of my earlier comment. Though instead of proving something to the backend I was thinking more in terms of the frontend trying to make sure its assumptions hold before the changes are applied. Different side of the same coin.

If we decide to go that route we have to be aware that this precludes any kind of merging of actions even in completely disjoint parts of the circuit as the backend will - as it does now - not have a clue about possible collisions say in terms of geometry. If we assume good connectivity and the "significant" update thingy I mentioned before I think this should still work nicely.

@christianbrugger
Copy link
Member Author

Another thought, what should happen if undo is not possible anymore? Let us say user moves big group of object from A to B. Then the other user moves an object in this area. How can the first user undo his change? Is it just denied?

@hacst
Copy link
Member

hacst commented Jan 9, 2015

That would be what I had in mind though if the frontend can offer something else the backend obviously won't stop him.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants