Fix undo bugs with edited_objs

Now it's stored as a collection property in the project properties, so in the most cases we delegate to Blender to ensure it's handled properly on undo and redo.
This commit is contained in:
Andrej730
2024-10-30 18:08:45 +05:00
parent 09df81ab15
commit 904eb9fae8
4 changed files with 53 additions and 7 deletions
+27 -2
View File
@@ -45,10 +45,14 @@ class OperationData(TypedDict):
obj: str
class EditObjectOperationData(TypedDict):
obj: str
class Operation(TypedDict):
rollback: Callable
commit: Callable
data: Union[OperationData, None]
data: Union[OperationData, EditObjectOperationData, None]
class TransactionStep(TypedDict):
@@ -295,7 +299,28 @@ class IfcStore:
if "guid" in data:
IfcStore.guid_map[data["guid"]] = obj
tool.Ifc.setup_listeners(obj)
# TODO We're handling id_map and guid_map, but what about edited_objs? This might cause big problems.
@staticmethod
def history_edit_object(obj: bpy.types.Object, *, finish_editing: bool) -> None:
if not IfcStore.history:
return
commit, rollback = IfcStore.commit_edit_object, IfcStore.rollback_edit_object
if finish_editing:
commit, rollback = rollback, commit
data = EditObjectOperationData(obj=obj.name)
IfcStore.history[-1]["operations"].append(Operation(rollback=rollback, commit=commit, data=data))
@staticmethod
def commit_edit_object(data: EditObjectOperationData) -> None:
obj = bpy.data.objects[data["obj"]]
IfcStore.edited_objs.add(obj)
@staticmethod
def rollback_edit_object(data: EditObjectOperationData) -> None:
obj = bpy.data.objects[data["obj"]]
IfcStore.edited_objs.discard(obj)
@staticmethod
def rollback_unlink_element(data: OperationData) -> None:
@@ -64,6 +64,7 @@ classes = (
prop.LibraryElement,
prop.FilterCategory,
prop.Link,
prop.EditedObj,
prop.BIMProjectProperties,
prop.MeasureToolSettings,
ui.BIM_MT_new_project,
@@ -126,6 +126,10 @@ class Link(PropertyGroup):
)
class EditedObj(PropertyGroup):
obj: PointerProperty(type=bpy.types.Object)
class BIMProjectProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing", default=False)
is_loading: BoolProperty(name="Is Loading", default=False)
@@ -226,6 +230,7 @@ class BIMProjectProperties(PropertyGroup):
queried_obj_root: bpy.props.PointerProperty(type=bpy.types.Object)
clipping_planes: bpy.props.CollectionProperty(type=ObjProperty)
clipping_planes_active: bpy.props.IntProperty(min=0, default=0, max=5)
edited_objs: bpy.props.CollectionProperty(type=EditedObj)
@property
def clipping_planes_objs(self):
+20 -5
View File
@@ -176,6 +176,16 @@ class Ifc(bonsai.core.tool.Ifc):
IfcStore.id_map[style.id()] = obj
cls.setup_listeners(obj)
IfcStore.edited_objs = set()
edited_objs = bpy.context.scene.BIMProjectProperties.edited_objs
for i in range(len(edited_objs))[::-1]:
obj = edited_objs[i].obj
if obj:
IfcStore.edited_objs.add(obj)
else:
# Object was removed.
edited_objs.remove(i)
@classmethod
def setup_listeners(cls, obj: IFC_CONNECTED_TYPE) -> None:
if isinstance(obj, bpy.types.Object):
@@ -200,17 +210,17 @@ class Ifc(bonsai.core.tool.Ifc):
changed geometry to IFC, we mark it as changed and then it's saved later
(typically during project save or switch_representation(should_sync_changes_first=True)).
Currently, underlying storage for edited objects, IfcStore.edited_objs, is not tracked
by undo system. So, it's error prone:
- undo after object was marked as edited, will keep it edited, adding unnecessary sync
- undo after object was unmarked as edited, will keep it unmarked, so edit geometry may be lost
Other caveat of using edited objects is that it won't have an effect for objects with openings,
since we can't deduce non-openings representation from edited representation with openings.
So, it's preferable not to use edited objects if object can have an opening. It's still can be used for spaces.
"""
if obj in IfcStore.edited_objs:
return
edited_objs = bpy.context.scene.BIMProjectProperties.edited_objs
edited_objs.add().obj = obj
IfcStore.edited_objs.add(obj)
IfcStore.history_edit_object(obj, finish_editing=False)
@classmethod
def finish_edit(cls, obj: bpy.types.Object) -> None:
@@ -218,7 +228,12 @@ class Ifc(bonsai.core.tool.Ifc):
Method is safe to use on an object that wasn't marked as edited before.
"""
if obj not in IfcStore.edited_objs:
return
edited_objs = bpy.context.scene.BIMProjectProperties.edited_objs
edited_objs.remove(next(i for i, o in enumerate(edited_objs) if o.obj == obj))
IfcStore.edited_objs.discard(obj)
IfcStore.history_edit_object(obj, finish_editing=True)
@classmethod
def resolve_uri(cls, uri: str) -> str: