diff --git a/src/blenderbim/blenderbim/bim/module/model/__init__.py b/src/blenderbim/blenderbim/bim/module/model/__init__.py index ea67281ce6..3e85b1cf59 100644 --- a/src/blenderbim/blenderbim/bim/module/model/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/model/__init__.py @@ -23,6 +23,7 @@ classes = ( product.AddEmptyType, product.AddConstrTypeInstance, product.DisplayConstrTypes, + product.ReinvokeOperator, product.AlignProduct, product.DynamicallyVoidProduct, workspace.Hotkey, diff --git a/src/blenderbim/blenderbim/bim/module/model/data.py b/src/blenderbim/blenderbim/bim/module/model/data.py index 5dc751ef8c..c2d4dc0974 100644 --- a/src/blenderbim/blenderbim/bim/module/model/data.py +++ b/src/blenderbim/blenderbim/bim/module/model/data.py @@ -131,11 +131,14 @@ class AuthoringData: if new_obj is not None: to_be_deleted = True obj = new_obj + obj.hide_set(True) obj.asset_mark() obj.asset_generate_preview() + blender33_or_above = bpy.app.version >= (3, 3, 0) + interval = 1e-4 - def wait_for_asset_previews_generation(check_interval_seconds=0.0001): - if bpy.app.is_job_running("RENDER_PREVIEW"): + def wait_for_asset_previews_generation(check_interval_seconds=interval): + if blender33_or_above and bpy.app.is_job_running("RENDER_PREVIEW"): return check_interval_seconds else: if ifc_class not in cls.props.constr_classes: @@ -157,7 +160,8 @@ class AuthoringData: collection.objects.unlink(obj) return None - bpy.app.timers.register(wait_for_asset_previews_generation) + first_interval = 0 if blender33_or_above else interval + bpy.app.timers.register(wait_for_asset_previews_generation, first_interval=first_interval) @classmethod def assetize_relating_type_from_selection(cls, browser=False): diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 424539c4c9..feea875d1c 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -18,6 +18,7 @@ import bpy import mathutils +from functools import reduce import ifcopenshell import ifcopenshell.api import ifcopenshell.util.system @@ -211,6 +212,44 @@ class DisplayConstrTypes(bpy.types.Operator): return {"FINISHED"} +class ReinvokeOperator(bpy.types.Operator): + bl_idname = "bim.reinvoke_operator" + bl_label = "Reinvoke Popup Operator" + bl_options = {"REGISTER"} + bl_description = "Reinvoke a popup operator" + operator: bpy.props.StringProperty() + mouse_x: bpy.props.IntProperty() + mouse_y: bpy.props.IntProperty() + + def execute(self, context): + return {"FINISHED"} + + def invoke(self, context, event): + event_mouse_x, event_mouse_y = event.mouse_x, event.mouse_y + window_mouse_x, window_mouse_y = self.mouse_x, self.mouse_y + if (event_mouse_x, event_mouse_y) == (10, 10): + return {"FINISHED"} + window = context.window + window.cursor_set("WAIT") + window.cursor_warp(10, 10) + run_operator = self.run_operator + operator = self.operator + + def reinvoke(): + window.cursor_warp(event_mouse_x, event_mouse_y) + kwargs = {} + if (window_mouse_x, window_mouse_y) != (0, 0): + kwargs.update({"mouse_x": window_mouse_x, "mouse_y": window_mouse_y}) + run_operator(operator, "INVOKE_DEFAULT", **kwargs) + + bpy.app.timers.register(reinvoke) + return {"FINISHED"} + + @staticmethod + def run_operator(operator, *args, **kwargs): + reduce(lambda x, arg: getattr(x, arg), operator.split("."), bpy.ops)(*args, **kwargs) + + class AlignProduct(bpy.types.Operator): bl_idname = "bim.align_product" bl_label = "Align Product" diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index a995e3290a..c531c1a3cb 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -41,23 +41,25 @@ def get_relating_type_browser(self, context): def update_icon_id(self, context, browser=False): - ifc_class = self.ifc_class_browser if browser else self.ifc_class - relating_type_id = self.relating_type_id_browser if browser else self.relating_type_id - # relating_type = AuthoringData.relating_type_name_by_id(ifc_class, relating_type_id) + if context == "lost_context" or (context.region is not None and context.region.type != "TOOL_HEADER"): + ifc_class = self.ifc_class_browser if browser else self.ifc_class + relating_type_id = self.relating_type_id_browser if browser else self.relating_type_id + # relating_type = AuthoringData.relating_type_name_by_id(ifc_class, relating_type_id) - if ifc_class not in self.constr_classes or relating_type_id not in self.constr_classes[ifc_class].constr_types: - try: - AuthoringData.assetize_relating_type_from_selection(browser=browser) - except ConstrTypeEntityNotFound: - return - - def set_icon(update_interval_seconds=0.0001): if ifc_class not in self.constr_classes or relating_type_id not in self.constr_classes[ifc_class].constr_types: - return update_interval_seconds - else: - self.icon_id = self.constr_classes[ifc_class].constr_types[relating_type_id].icon_id + try: + AuthoringData.assetize_relating_type_from_selection(browser=browser) + except ConstrTypeEntityNotFound: + return - bpy.app.timers.register(set_icon) + def set_icon(update_interval_seconds=1e-4): + if (ifc_class not in self.constr_classes + or relating_type_id not in self.constr_classes[ifc_class].constr_types): + return update_interval_seconds + else: + self.icon_id = self.constr_classes[ifc_class].constr_types[relating_type_id].icon_id + + bpy.app.timers.register(set_icon) def update_ifc_class(self, context): @@ -68,15 +70,16 @@ def update_ifc_class(self, context): def update_ifc_class_browser(self, context): - AuthoringData.load_ifc_classes() - AuthoringData.load_relating_types_browser() - if self.updating: - return - ifc_class = self.ifc_class_browser - constr_class_info = AuthoringData.constr_class_info(ifc_class) + if context.region is not None and context.region.type != "TOOL_HEADER": + AuthoringData.load_ifc_classes() + AuthoringData.load_relating_types_browser() + if self.updating: + return + ifc_class = self.ifc_class_browser + constr_class_info = AuthoringData.constr_class_info(ifc_class) - if constr_class_info is None or not constr_class_info.fully_loaded: - AuthoringData.assetize_constr_class(ifc_class) + if constr_class_info is None or not constr_class_info.fully_loaded: + AuthoringData.assetize_constr_class(ifc_class) def update_relating_type(self, context): @@ -103,13 +106,14 @@ def get_constr_class_info(props, ifc_class): def update_preview_multiple(self, context): - if self.preview_multiple_constr_types: - ifc_class = self.ifc_class - constr_class_info = get_constr_class_info(self, ifc_class) - if constr_class_info is None or not constr_class_info.fully_loaded: - AuthoringData.assetize_constr_class(ifc_class) - else: - update_relating_type(self, context) + if context.region is not None and context.region.type != "TOOL_HEADER": + if self.preview_multiple_constr_types: + ifc_class = self.ifc_class + constr_class_info = get_constr_class_info(self, ifc_class) + if constr_class_info is None or not constr_class_info.fully_loaded: + AuthoringData.assetize_constr_class(ifc_class) + else: + update_relating_type(self, context) class ConstrTypeInfo(PropertyGroup): diff --git a/src/blenderbim/blenderbim/bim/module/model/ui.py b/src/blenderbim/blenderbim/bim/module/model/ui.py index c42364ce58..2652c9447d 100644 --- a/src/blenderbim/blenderbim/bim/module/model/ui.py +++ b/src/blenderbim/blenderbim/bim/module/model/ui.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +import bpy from bpy.types import Panel, Operator from blenderbim.bim.module.model.data import AuthoringData from blenderbim.bim.helper import prop_with_search, close_operator_panel @@ -40,14 +41,19 @@ class DisplayConstrTypesUI(Operator): bl_label = "Browse Construction Types" bl_options = {"REGISTER"} bl_description = "Display all available Construction Types to add new instances" + mouse_x: bpy.props.IntProperty(default=0) + mouse_y: bpy.props.IntProperty(default=0) def execute(self, context): return {"FINISHED"} def invoke(self, context, event): + if (self.mouse_x, self.mouse_y) == (0, 0): + self.mouse_x, self.mouse_y = context.window.x, context.window.y return context.window_manager.invoke_popup(self, width=550) def draw(self, context): + bpy.context.window.cursor_set("DEFAULT") props = context.scene.BIMModelProperties if AuthoringData.data["ifc_classes"]: row = self.layout.row() @@ -68,9 +74,19 @@ class DisplayConstrTypesUI(Operator): row = box.row() if ifc_class in props.constr_classes: constr_class_info = props.constr_classes[ifc_class] - if relating_type_id in constr_class_info.constr_types: - icon_id = constr_class_info.constr_types[relating_type_id].icon_id + constr_types_info = constr_class_info.constr_types + if relating_type_id in constr_types_info: + icon_id = constr_types_info[relating_type_id].icon_id row.template_icon(icon_value=icon_id, scale=6.0) + else: + mouse_x, mouse_y = self.mouse_x, self.mouse_y + + def run_operator(): + bpy.ops.bim.reinvoke_operator( + "INVOKE_DEFAULT", operator="bim.display_constr_types_ui", mouse_x=mouse_x, mouse_y=mouse_y + ) + bpy.app.timers.register(run_operator) + row = box.row() op = row.operator("bim.add_constr_type_instance", icon="ADD") op.from_invoke = True diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index 8f9e1cbb6d..cdd4cb2326 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -74,10 +74,10 @@ class BimTool(WorkSpaceTool): AuthoringData.data["relating_types_ids"] if "relating_types_ids" in AuthoringData.data else False ) - if ifc_classes and relating_types_ids and not props.icon_id: + if ifc_classes and relating_types_ids and not props.icon_id and not is_tool_header: # hack Dion won't like to show a preview also on the first time the sidebar is shown - bpy.app.timers.register(lambda: prop.update_ifc_class_browser(props, context)) - bpy.app.timers.register(lambda: prop.update_relating_type(props, context)) + bpy.app.timers.register(lambda: prop.update_ifc_class(props, "lost_context")) + bpy.app.timers.register(lambda: prop.update_relating_type(props, "lost_context")) ifc_class = props.ifc_class relating_type_id = props.relating_type_id