Page MenuHome

Add `forget` option for `bpy.ops.ed.undo()`
Needs ReviewPublic

Authored by Stanislav Blinov (radcapricorn) on Tue, Oct 29, 1:33 AM.

Details

Summary

When forget is True, the step will be undone and removed from redo history. This is useful for modal operators that want to use undo and support cancellation, so that on cancel they can remove their undo step entirely.

Diff Detail

Repository
rB Blender

Event Timeline

This is more of a design task, since I'm not sure we want to encourage operators using the undo stack for their execution.

  • Modal operators using undo/redo at all is questionable (currently we don't do this as far as I know).
  • Using a "forget" property to control internal execution behavior isn't a good fit since these are meant to control operator behavior. It would probably be better if it's an operator flag.

Could you give some examples of where you would use this?

AFAIK, internally you do do this via internal undo for pretty much every interactive operator. But that's not exposed to Python :(

As for an example, I'm writing a "Hinge", which is a user-friendlier wrapper over spin.

The gist of it is:

def modal(self, context, event):
    #...

    if event.type == 'LEFTMOUSE':
        return {'FINISHED'}
    elif event.type in {'RIGHTMOUSE', 'ESC'}:
        # This actually leaves last edit in the undo history,
        # which the user is likely to just overwrite with their next action,
        # so not a huge problem. But ideally be nice to have mesh stashing, or something like bpy.ops.ed.undo_and_forget(),
        if self.modal_input.active:
            bpy.ops.ed.undo()
        return {'CANCELLED'}
   
    if event.type in ('MOUSEMOVE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'):
        # ...
        # input processing snipped
        # ...
        if self.modal_input.active:
            bpy.ops.ed.undo()
        mesh = context.object.data
        bm = bmesh.from_edit_mesh(mesh)
        recalc_hinge_mesh(self.modal_input, bm)
        bpy.ops.ed.undo_push(message="Hinge Modal")
        bmesh.update_edit_mesh(mesh, True, True)
        self.modal_input.active = True

    return {'RUNNING_MODAL'}

IOW, as long as user is interacting, undo previous edit and commit a new one. But this approach leaves last undo step in history, even if user cancels the operator.
Indeed, a more appropriate feature would require some new API.

Blender's own modal operators don't use the undo stack. Although they do use functions in C that can backup/restore state which Python doesn't have access to.

I'd rather avoid using undo for modal operators, it's likely to be slow and cause unnecessary calculations.

Instead, we could expose functionality that can backup/restore data to Python too.

I agree in principle, though I'd rather like some unified API for backup/restore that could be used with other edit modes as well. Do you have any advice on how to proceed from here?

BTW, in this particular case, the cost of spin itself seems to dwarf undo by a huge margin.