Fix critical undo sync bug related to invoking instead of executing. See notes.

Currently, the undo system works by wrapping the execute operator. What was not considered was that execute() isn't the only entry point to operators. Operators can be invoked. If an invoked operator calls a bpy.ops.bim.something in its invoke() function, then bpy.ops.bim.something will start and finish its own transaction, and then when the invoke finishes and finally runs execute(), another transaction will be started and finished. This means that we get at least one extra transaction logged (one per any call to bpy.ops.bim.foo in invoke()).

This mismatch of number of transactions and undo stack means that when the user presses ctrl-z, the undo doesn't actually occur. This leads to a sync bug.

To be safe, all IFC invokes must be wrapped. This will take some time to review so expect another commit soon.
This commit is contained in:
Dion Moult
2023-07-09 22:29:49 +10:00
parent 05a3a33a9a
commit 52ef8a6042
2 changed files with 11 additions and 2 deletions
+5 -2
View File
@@ -400,7 +400,7 @@ class IfcStore:
)
@staticmethod
def execute_ifc_operator(operator, context):
def execute_ifc_operator(operator, context, is_invoke=False):
is_top_level_operator = not bool(IfcStore.current_transaction)
if is_top_level_operator:
@@ -411,7 +411,10 @@ class IfcStore:
else:
operator.transaction_key = IfcStore.current_transaction
result = getattr(operator, "_execute")(context)
if is_invoke:
result = getattr(operator, "_invoke")(context, None)
else:
result = getattr(operator, "_execute")(context)
if is_top_level_operator:
IfcStore.get_file().end_transaction()
@@ -1323,6 +1323,9 @@ class OverrideModeSetEdit(bpy.types.Operator):
return {"FINISHED"}
def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
def _invoke(self, context, event):
if not tool.Ifc.get():
return bpy.ops.object.mode_set(mode="EDIT", toggle=True)
return self.execute(context)
@@ -1372,6 +1375,9 @@ class OverrideModeSetObject(bpy.types.Operator):
row.label(text="No Geometry Found: Object will revert to previous state.")
def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
def _invoke(self, context, event):
self.is_valid = True
self.should_save = True