diff --git a/src/blenderbim/blenderbim/bim/module/model/__init__.py b/src/blenderbim/blenderbim/bim/module/model/__init__.py index ec11b68f37..0c79b36167 100644 --- a/src/blenderbim/blenderbim/bim/module/model/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/model/__init__.py @@ -59,6 +59,7 @@ classes = ( slab.SetArcIndex, prop.ConstrTypeInfo, prop.ConstrClassInfo, + prop.ConstrBrowserState, prop.BIMModelProperties, ui.BIM_PT_authoring, ui.DisplayConstrTypesUI, diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index feea875d1c..ffad2c118f 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -31,6 +31,7 @@ import blenderbim.core.geometry from . import wall, slab, profile, mep from blenderbim.bim.ifc import IfcStore from blenderbim.bim.module.model.data import AuthoringData +from blenderbim.bim.module.model.prop import store_cursor_position from ifcopenshell.api.pset.data import Data as PsetData from mathutils import Vector, Matrix from bpy_extras.object_utils import AddObjectHelper @@ -218,33 +219,37 @@ class ReinvokeOperator(bpy.types.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"} + browser_state = context.scene.BIMModelProperties.constr_browser_state + store_cursor_position(context, event, window=False) + cursor_x, cursor_y = browser_state.cursor_x, browser_state.cursor_y + window_x, window_y = browser_state.window_x, browser_state.window_y window = context.window - window.cursor_set("WAIT") - window.cursor_warp(10, 10) - run_operator = self.run_operator operator = self.operator + self.move_cursor_away(context, window) + + def move_cursor_to_window(): + window.cursor_warp(window_x, window_y) + + def run_operator(operator, *args, **kwargs): + reduce(lambda x, arg: getattr(x, arg), operator.split("."), bpy.ops)(*args, **kwargs) 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) + run_operator(operator, "INVOKE_DEFAULT", reinvoked=True) + window.cursor_warp(cursor_x, cursor_y) - bpy.app.timers.register(reinvoke) + bpy.app.timers.register(move_cursor_to_window, first_interval=browser_state.update_delay) + bpy.app.timers.register(reinvoke, first_interval=3*browser_state.update_delay) return {"FINISHED"} + def move_cursor_away(self, context, window): # closes current popup + browser_state = context.scene.BIMModelProperties.constr_browser_state + window.cursor_warp(browser_state.far_away_x, browser_state.far_away_y) + @staticmethod def run_operator(operator, *args, **kwargs): reduce(lambda x, arg: getattr(x, arg), operator.split("."), bpy.ops)(*args, **kwargs) diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index be5c9c9f68..284c7012fa 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -41,6 +41,14 @@ def get_relating_type_browser(self, context): return AuthoringData.data["relating_types_ids_browser"] +def store_cursor_position(context, event, cursor=True, window=True): + browser_state = context.scene.BIMModelProperties.constr_browser_state + if cursor: + browser_state.cursor_x, browser_state.cursor_y = event.mouse_x, event.mouse_y + if window: + browser_state.window_x, browser_state.window_y = event.mouse_x, event.mouse_y + + def update_icon_id(self, context, browser=False): 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 @@ -129,6 +137,17 @@ class ConstrClassInfo(PropertyGroup): fully_loaded: bpy.props.BoolProperty(default=False) +class ConstrBrowserState(PropertyGroup): + cursor_x: bpy.props.IntProperty() + cursor_y: bpy.props.IntProperty() + window_x: bpy.props.IntProperty() + window_y: bpy.props.IntProperty() + far_away_x: bpy.props.IntProperty(default=10) # lower left corner to temporarily warp the mouse + far_away_y: bpy.props.IntProperty(default=10) # useful to close popup operators + updating: bpy.props.BoolProperty() + update_delay: bpy.props.FloatProperty(default=3e-2) + + class BIMModelProperties(PropertyGroup): ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="Construction Class", update=update_ifc_class) ifc_class_browser: bpy.props.EnumProperty( @@ -151,6 +170,7 @@ class BIMModelProperties(PropertyGroup): occurrence_name_function: bpy.props.StringProperty(name="Occurrence Name Function") getter_enum = {"ifc_class": get_ifc_class, "relating_type": get_relating_type} constr_classes: bpy.props.CollectionProperty(type=ConstrClassInfo) + constr_browser_state: bpy.props.PointerProperty(type=ConstrBrowserState) extrusion_depth: bpy.props.FloatProperty(default=42.0) cardinal_point: bpy.props.EnumProperty( items=( diff --git a/src/blenderbim/blenderbim/bim/module/model/ui.py b/src/blenderbim/blenderbim/bim/module/model/ui.py index 793bc170de..4605acc844 100644 --- a/src/blenderbim/blenderbim/bim/module/model/ui.py +++ b/src/blenderbim/blenderbim/bim/module/model/ui.py @@ -19,6 +19,7 @@ import bpy from bpy.types import Panel, Operator, Menu from blenderbim.bim.module.model.data import AuthoringData +from blenderbim.bim.module.model.prop import store_cursor_position from blenderbim.bim.helper import prop_with_search, close_operator_panel @@ -41,20 +42,34 @@ 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) + reinvoked: bpy.props.BoolProperty(default=False) 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 + store_cursor_position(context, event, window=not self.reinvoked) return context.window_manager.invoke_popup(self, width=550) + def reinvoke(self, context): + browser_state = context.scene.BIMModelProperties.constr_browser_state + + def set_updating_transaction(): + browser_state.updating = True + + def run_operator(): + bpy.ops.bim.reinvoke_operator( + "INVOKE_DEFAULT", operator="bim.display_constr_types_ui" + ) + browser_state.updating = False + + if not browser_state.updating: + bpy.app.timers.register(set_updating_transaction) + bpy.app.timers.register(run_operator, first_interval=browser_state.update_delay) + def draw(self, context): - bpy.context.window.cursor_set("DEFAULT") props = context.scene.BIMModelProperties + if AuthoringData.data["ifc_classes"]: row = self.layout.row() row.label(text="", icon="FILE_VOLUME") @@ -65,6 +80,7 @@ class DisplayConstrTypesUI(Operator): flow = self.layout.grid_flow(row_major=True, columns=num_cols, even_columns=True, even_rows=True, align=True) relating_types = AuthoringData.relating_types_browser() num_types = len(relating_types) + for idx, (relating_type_id, name, desc) in enumerate(relating_types): outer_col = flow.column() box = outer_col.box() @@ -72,31 +88,32 @@ class DisplayConstrTypesUI(Operator): row.label(text=name, icon="FILE_3D") row.alignment = "CENTER" row = box.row() + if ifc_class in props.constr_classes: constr_class_info = props.constr_classes[ifc_class] 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) + self.reinvoke(context) + return row = box.row() op = row.operator("bim.add_constr_type_instance", icon="ADD") op.from_invoke = True op.ifc_class = ifc_class + if relating_type_id.isnumeric(): op.relating_type_id = int(relating_type_id) + last_row_cols = num_types % num_cols + if last_row_cols != 0: for _ in range(num_cols - last_row_cols): flow.column() + row = self.layout.row() row.alignment = "RIGHT" row.operator("bim.help_relating_types", text="", icon="QUESTION")