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

Refactor Undo/Redo #1060

Open
corranwebster opened this issue Jul 23, 2020 · 1 comment
Open

Refactor Undo/Redo #1060

corranwebster opened this issue Jul 23, 2020 · 1 comment
Labels
difficulty: intermediate Issues which need some context about the package. type: important type: refactor Issues related to refactoring

Comments

@corranwebster
Copy link
Contributor

The undo/redo code is not particularly clean or well-written. It is also not API-compatible with the apptools Undo/Redo system.

A proper re-write might mean bringing apptools Undo/Redo into Pyface (at least the core, but possibly all of it, so it can be used there) and then making the TraitsUI implement or adapt to the API for its undo/redo, so that it can integrate with application-level undo/redo.

While I suspect that this code is not heavily used externally to TraitsUI, there may need to take some care not to break the API exposed to editors and handlers without a clear migration path.

@corranwebster corranwebster added type: refactor Issues related to refactoring type: important difficulty: intermediate Issues which need some context about the package. labels Jul 23, 2020
@corranwebster corranwebster added this to the Release 7.1 milestone Jul 23, 2020
@corranwebster corranwebster removed this from the Release 7.1 milestone Jan 30, 2021
@corranwebster
Copy link
Contributor Author

As of this writing, we have moved the AppTools Undo/Redo into Pyface and made a release with this change, so it is now easily available to TraitsUI. There is also #1510 pending, which adds testing, bugfixes, and makes the UndoItem API compatible with the AbstractCommand API.

The next step after this is probably to integrate an UndoManager into the UI class and refactor the UndoHistory class so that instead of a list of lists to hold the history it uses the UndoManager. Almost all the logic of of the UndoHistory class is already implemented by the UndoManager, so UndoHistory can become a thin wrapper around this. This change permits an application or Task's UndoManager to be shared with TraitsUI GUIs embedded into the view, if desired. This is a potentially backwards-incompatible change, but only to code which is directly manipulating the history list - which really should not be happening. This can probably be included in a minor release without breaking existing code.

One slight mismatch between the APIs is how to group multiple commands into a single command that can be performed with one undo/redo action. The Pyface Undo/Redo handles this with the begin_macro/end_macro commands on the command stack; while UndoHistory provides the extend method and argument that adds extra commands to the current list of commands (effectively every command is a macro with at least one item) and whether or not to extend is usually controlled by the do_undoable code in the UI module. At a first cut, we can support the extend API, but in the longer term it may make sense to instead refactor do_undoable and other places where extend is used to instead use begin/end macro. The latter may again break third-party code which is using the extend method (most likely custom editor classes, so a bit more risky to change), but it may be possible to keep the extend API available while replacing most uses with the better API and removing the extend API in the next release.

The final change is more substantive, and should perhaps be done as part of the effort for the Traits 8 release. Currently UndoItem instances are created in reaction to trait changes (ie. after the changes have occurred) rather than driving the changes themselves: the do method is essentially a no-op. It should be feasible to change the internals of TraitsUI to be more directly command-driven under the covers: setting the value of an Editor (and perhaps some other parts of the editor state) should generate a Command to call the appropriate setattr method, and that is executed by pushing it onto the command stack. This should work well under the assumption that any change made to a trait is reversible - if the value is set back to the old value, then the all the associated state will reset itself automatically. More complex situations, especially those where setting the trait value triggers an expensive computation, will need custom commands to be written, and these can be supplied to the editor to use instead.

Eventually it should be possible to retire UndoHandler and simply use the UndoManager directly in every case.

corranwebster added a commit that referenced this issue Mar 21, 2023
Fixes #1515.  Also removes a no-longer used `history` trait.

This completes the transition of TraitsUI undo/redo to share the same basis as the Pyface/Apptools undo/redo.

There is still an issue in #1060 that is unresolved which is about refactoring TraitsUI to be more command-stack based, rather than building the command stack reactively.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
difficulty: intermediate Issues which need some context about the package. type: important type: refactor Issues related to refactoring
Projects
None yet
Development

No branches or pull requests

1 participant