Exiting current editing decorator if user switches from EDIT mode

Switching from EDIT mode acts like canceling editing which seems intuitive.

Now it's also possible to add similar thing to other use of decorators - beware of the possible Blender bug - mentioned it in `ProfileDecorator.install description`

Added this change to editing of profiles, extrusion axis, roof and railing paths, extrusion profiles.
This commit is contained in:
Andrej730
2023-03-28 16:53:09 +05:00
parent c725eeb5db
commit 75c98e5b00
6 changed files with 111 additions and 70 deletions
@@ -42,12 +42,16 @@ class ProfileDecorator:
installed = None
@classmethod
def install(cls, context, get_custom_bmesh=None, draw_faces=False):
def install(cls, context, get_custom_bmesh=None, draw_faces=False, exit_edit_mode_callback=None):
"""Note that operators that change mesh in `exit_edit_mode_callback` can freeze blender.
The workaround is to move their code to function and use it for callback.
Example: https://devtalk.blender.org/t/calling-operator-that-saves-bmesh-freezes-blender-forever/28595"""
if cls.installed:
cls.uninstall()
handler = cls()
cls.installed = SpaceView3D.draw_handler_add(
handler, (context, get_custom_bmesh, draw_faces), "WINDOW", "POST_VIEW"
handler, (context, get_custom_bmesh, draw_faces, exit_edit_mode_callback), "WINDOW", "POST_VIEW"
)
@classmethod
@@ -76,10 +80,13 @@ class ProfileDecorator:
face_indices = [[v.index for v in f.verts] for f in traingulated_bm.faces]
self.create_batch("TRIS", vertices_coords, faces_color, face_indices)
def __call__(self, context, get_custom_bmesh=None, draw_faces=False):
def __call__(self, context, get_custom_bmesh=None, draw_faces=False, exit_edit_mode_callback=None):
obj = context.active_object
if obj.mode != "EDIT":
if exit_edit_mode_callback:
ProfileDecorator.uninstall()
exit_edit_mode_callback()
return
if get_custom_bmesh:
@@ -938,12 +938,32 @@ class EnableEditingExtrusionAxis(bpy.types.Operator, tool.Ifc.Operator):
tool.Model.import_axis([Vector((0, 0, 0)), direction * extrusion.Depth], obj=obj, position=position)
bpy.ops.object.mode_set(mode="EDIT")
ProfileDecorator.install(context)
ProfileDecorator.install(context, exit_edit_mode_callback=lambda: disable_editing_extrusion_axis(context))
if not bpy.app.background:
bpy.ops.wm.tool_set_by_id(tool.Blender.get_viewport_context(), name="bim.cad_tool")
return {"FINISHED"}
def disable_editing_extrusion_axis(context):
ProfileDecorator.uninstall()
bpy.ops.object.mode_set(mode="OBJECT")
obj = context.active_object
element = tool.Ifc.get_entity(obj)
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
blenderbim.core.geometry.switch_representation(
tool.Ifc,
tool.Geometry,
obj=obj,
representation=body,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
)
return {"FINISHED"}
class DisableEditingExtrusionAxis(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_editing_extrusion_axis"
bl_label = "Disable Editing Extrusion Axis"
@@ -954,23 +974,7 @@ class DisableEditingExtrusionAxis(bpy.types.Operator, tool.Ifc.Operator):
return context.selected_objects
def _execute(self, context):
ProfileDecorator.uninstall()
bpy.ops.object.mode_set(mode="OBJECT")
obj = context.active_object
element = tool.Ifc.get_entity(obj)
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
blenderbim.core.geometry.switch_representation(
tool.Ifc,
tool.Geometry,
obj=obj,
representation=body,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
)
return {"FINISHED"}
return disable_editing_extrusion_axis()
class EditExtrusionAxis(bpy.types.Operator, tool.Ifc.Operator):
@@ -364,26 +364,30 @@ class EnableEditingRailingPath(bpy.types.Operator, tool.Ifc.Operator):
if bpy.context.object.mode != "EDIT":
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.wm.tool_set_by_id(tool.Blender.get_viewport_context(), name="bim.cad_tool")
ProfileDecorator.install(context)
ProfileDecorator.install(context, exit_edit_mode_callback=lambda: cancel_editing_railing_path(context))
return {"FINISHED"}
def cancel_editing_railing_path(context):
obj = context.active_object
props = obj.BIMRailingProperties
ProfileDecorator.uninstall()
props.is_editing_path = False
update_railing_modifier_bmesh(context)
if bpy.context.object.mode == "EDIT":
bpy.ops.object.mode_set(mode="OBJECT")
return {"FINISHED"}
class CancelEditingRailingPath(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.cancel_editing_railing_path"
bl_label = "Cancel Editing Railing Path"
bl_options = {"REGISTER"}
def _execute(self, context):
obj = context.active_object
props = obj.BIMRailingProperties
ProfileDecorator.uninstall()
props.is_editing_path = False
update_railing_modifier_bmesh(context)
if bpy.context.object.mode == "EDIT":
bpy.ops.object.mode_set(mode="OBJECT")
return {"FINISHED"}
return cancel_editing_railing_path(context)
class FinishEditingRailingPath(bpy.types.Operator, tool.Ifc.Operator):
@@ -215,7 +215,7 @@ def generate_hiped_roof_bmesh(bm, mode="ANGLE", height=1.0, angle=pi / 18, mutat
# find footprint edges
for edge in bm.edges:
if all(float_is_zero(v.co.z-footprint_z) for v in edge.verts):
if all(float_is_zero(v.co.z - footprint_z) for v in edge.verts):
footprint_edges.append(edge)
footprint_verts.update(edge.verts)
@@ -557,26 +557,35 @@ class EnableEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
tool.Blender.bmesh_join(main_bm, second_bm, callback=mark_preview_edges)
return main_bm
ProfileDecorator.install(context, get_custom_bmesh, draw_faces=True)
ProfileDecorator.install(
context,
get_custom_bmesh,
draw_faces=True,
exit_edit_mode_callback=lambda: cancel_editing_roof_path(context),
)
return {"FINISHED"}
def cancel_editing_roof_path(context):
obj = context.active_object
props = obj.BIMRoofProperties
ProfileDecorator.uninstall()
props.is_editing_path = False
update_roof_modifier_bmesh(context)
if bpy.context.object.mode == "EDIT":
bpy.ops.object.mode_set(mode="OBJECT")
return {"FINISHED"}
class CancelEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.cancel_editing_roof_path"
bl_label = "Cancel Editing Roof Path"
bl_options = {"REGISTER"}
def _execute(self, context):
obj = context.active_object
props = obj.BIMRoofProperties
ProfileDecorator.uninstall()
props.is_editing_path = False
update_roof_modifier_bmesh(context)
if bpy.context.object.mode == "EDIT":
bpy.ops.object.mode_set(mode="OBJECT")
return {"FINISHED"}
return cancel_editing_roof_path(context)
class FinishEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
@@ -554,6 +554,26 @@ class DisableEditingSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator
return {"FINISHED"}
def disable_editing_extrusion_profile(context):
ProfileDecorator.uninstall()
bpy.ops.object.mode_set(mode="OBJECT")
obj = context.active_object
element = tool.Ifc.get_entity(obj)
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
blenderbim.core.geometry.switch_representation(
tool.Ifc,
tool.Geometry,
obj=obj,
representation=body,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
)
return {"FINISHED"}
class DisableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_editing_extrusion_profile"
bl_label = "Disable Editing Extrusion Profile"
@@ -564,23 +584,7 @@ class DisableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
return context.selected_objects
def _execute(self, context):
ProfileDecorator.uninstall()
bpy.ops.object.mode_set(mode="OBJECT")
obj = context.active_object
element = tool.Ifc.get_entity(obj)
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
blenderbim.core.geometry.switch_representation(
tool.Ifc,
tool.Geometry,
obj=obj,
representation=body,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
)
return {"FINISHED"}
return disable_editing_extrusion_profile(context)
class EnableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
@@ -611,7 +615,7 @@ class EnableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
tool.Model.import_profile(extrusion.SweptArea, obj=obj, position=position)
bpy.ops.object.mode_set(mode="EDIT")
ProfileDecorator.install(context)
ProfileDecorator.install(context, exit_edit_mode_callback=lambda: disable_editing_extrusion_profile(context))
if not bpy.app.background:
bpy.ops.wm.tool_set_by_id(tool.Blender.get_viewport_context(), name="bim.cad_tool")
return {"FINISHED"}
@@ -648,7 +652,9 @@ class EditExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
self.layout.label(text="INVALID PROFILE")
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
ProfileDecorator.install(context)
ProfileDecorator.install(
context, exit_edit_mode_callback=lambda: disable_editing_extrusion_profile(context)
)
bpy.ops.object.mode_set(mode="EDIT")
return
@@ -23,6 +23,7 @@ import blenderbim.tool as tool
import blenderbim.bim.module.model.profile as model_profile
from blenderbim.bim.module.model.decorator import ProfileDecorator
from blenderbim.bim.module.profile.prop import generate_thumbnail_for_active_profile
from blenderbim.bim.module.profile.data import refresh
class LoadProfiles(bpy.types.Operator):
@@ -138,21 +139,29 @@ class EnableEditingArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
bpy.context.scene.collection.objects.link(obj)
bpy.context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode="EDIT")
ProfileDecorator.install(context)
ProfileDecorator.install(context, exit_edit_mode_callback=lambda: disable_editing_arbitrary_profile(context))
bpy.ops.wm.tool_set_by_id(tool.Blender.get_viewport_context(), name="bim.cad_tool")
def disable_editing_arbitrary_profile(context):
obj = context.active_object
if obj and obj.data and obj.data.BIMMeshProperties.subshape_type == "PROFILE":
ProfileDecorator.uninstall()
bpy.ops.object.mode_set(mode="OBJECT")
bpy.data.objects.remove(obj)
# need to update profile manager ui
# if this was called from decorator
refresh()
class DisableEditingArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_editing_arbitrary_profile"
bl_label = "Disable Editing Arbitrary Profile"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
obj = context.active_object
if obj and obj.data and obj.data.BIMMeshProperties.subshape_type == "PROFILE":
ProfileDecorator.uninstall()
bpy.ops.object.mode_set(mode="OBJECT")
bpy.data.objects.remove(obj)
return disable_editing_arbitrary_profile(context)
class EditArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
@@ -176,7 +185,9 @@ class EditArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
self.layout.label(text="INVALID PROFILE: " + indices[1])
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
ProfileDecorator.install(context)
ProfileDecorator.install(
context, exit_edit_mode_callback=lambda: disable_editing_arbitrary_profile(context)
)
bpy.ops.object.mode_set(mode="EDIT")
return