From 4f4c557f61bb217c26b96fdc9f1feac47d4a8592 Mon Sep 17 00:00:00 2001 From: Gorgious56 Date: Fri, 10 Jan 2025 14:24:33 +0100 Subject: [PATCH] Another attempt. Went back to previous toolbar : icon + type name + dot grid icon which all launch the type manager. Validating the type manager popup now only closes it. Functionally is the same thing as clicking Cancel. Don't really know what to do to fix it. Does cancel make sense in the type manager ? Do we want to be able to undo what is possible to be done from within the type manager ? You can change the active type either with the dropdown in the popup and in the grid flow. I changed it so that changing the type in the dropdown jumps to the corresponding page. BTW first time using github copilot to figure out the maths and it worked magically :) . I'm not against removing the dropdown but I feel like it's nice to see a plain list to choose from. You can input a specific page number or scrub the page number field. Might be usefull if user has dozens of pages to flip through. Right now the search is very simple, it does not implement fuzzy search or anything fancy with * --- src/bonsai/bonsai/bim/module/model/data.py | 22 ++++++---- src/bonsai/bonsai/bim/module/model/product.py | 2 +- src/bonsai/bonsai/bim/module/model/prop.py | 8 +++- src/bonsai/bonsai/bim/module/model/ui.py | 43 +++++++++++-------- .../bonsai/bim/module/model/workspace.py | 10 ++--- 5 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/model/data.py b/src/bonsai/bonsai/bim/module/model/data.py index 9a37bc55fa..c19e21a6dc 100644 --- a/src/bonsai/bonsai/bim/module/model/data.py +++ b/src/bonsai/bonsai/bim/module/model/data.py @@ -58,14 +58,15 @@ class AuthoringData: cls.data["ifc_classes"] = cls.ifc_classes() cls.data["ifc_class_current"] = cls.ifc_class_current() # Make sure .ifc_classes() was run before next lines - cls.data["filtered_type_elements"] = cls.filtered_type_elements() - # Make sure .filtered_type_elements() was run before next lines + cls.data["type_elements"] = cls.type_elements() + cls.data["type_elements_filtered"] = cls.type_elements_filtered() cls.data["relating_type_id"] = cls.relating_type_id() # Make sure .relating_type_id() was run before next lines cls.data["relating_type_id_current"] = cls.relating_type_id_current() cls.data["relating_type_name"] = cls.relating_type_name() cls.data["relating_type_description"] = cls.relating_type_description() cls.data["predefined_type"] = cls.predefined_type() + # Make sure .type_elements_filtered() was run before next lines cls.data["total_types"] = cls.total_types() cls.data["total_pages"] = cls.total_pages() # Only after .total_types() cls.data["next_page"] = cls.next_page() @@ -116,7 +117,7 @@ class AuthoringData: @classmethod def total_types(cls): - return len(cls.data["filtered_type_elements"]) + return len(cls.data["type_elements_filtered"]) @classmethod def total_pages(cls): @@ -134,19 +135,24 @@ class AuthoringData: return cls.props.type_page - 1 @classmethod - def filtered_type_elements(cls): + def type_elements(cls): ifc_class = cls.data["ifc_class_current"] if not ifc_class: return [] elements = list(tool.Ifc.get().by_type(ifc_class)) - if cls.props.search_name: - elements = [e for e in elements if cls.props.search_name.lower() in (e.Name or "Unnamed").lower()] return natsorted(elements, key=lambda s: (s.Name or "Unnamed").lower()) + @classmethod + def type_elements_filtered(cls): + elements = cls.data["type_elements"] + if cls.props.search_name: + return [e for e in elements if cls.props.search_name.lower() in (e.Name or "Unnamed").lower()] + return elements + @classmethod def paginated_relating_types(cls): results = [] - elements = cls.data["filtered_type_elements"] + elements = cls.data["type_elements_filtered"] elements = elements[(cls.props.type_page - 1) * cls.types_per_page : cls.props.type_page * cls.types_per_page] for element in elements: predefined_type = ifcopenshell.util.element.get_predefined_type(element) @@ -273,7 +279,7 @@ class AuthoringData: @classmethod def relating_type_id(cls): - elements = cls.data["filtered_type_elements"] + elements = cls.data["type_elements"] return [(str(e.id()), e.Name or "Unnamed", e.Description or "") for e in elements] @classmethod diff --git a/src/bonsai/bonsai/bim/module/model/product.py b/src/bonsai/bonsai/bim/module/model/product.py index 9e9bafcaba..7d76e47d4a 100644 --- a/src/bonsai/bonsai/bim/module/model/product.py +++ b/src/bonsai/bonsai/bim/module/model/product.py @@ -568,7 +568,7 @@ class LoadTypeThumbnails(bpy.types.Operator, tool.Ifc.Operator): # Large projects have hundreds of types which can lead to unnecessary lag. if not AuthoringData.is_loaded: AuthoringData.load() - queue = AuthoringData.data["filtered_type_elements"] + queue = AuthoringData.data["type_elements_filtered"] if self.limit: queue = queue[self.offset : self.offset + self.limit] else: diff --git a/src/bonsai/bonsai/bim/module/model/prop.py b/src/bonsai/bonsai/bim/module/model/prop.py index c9f1de899c..3a164477d6 100644 --- a/src/bonsai/bonsai/bim/module/model/prop.py +++ b/src/bonsai/bonsai/bim/module/model/prop.py @@ -65,10 +65,14 @@ def update_relating_type_id(self, context): AuthoringData.data["relating_type_name"] = AuthoringData.relating_type_name() AuthoringData.data["type_thumbnail"] = AuthoringData.type_thumbnail() AuthoringData.data["predefined_type"] = AuthoringData.predefined_type() + self.type_page = [e[0] for e in AuthoringData.data["relating_type_id"]].index(self.relating_type_id) // 9 + 1 def update_type_page(self, context): AuthoringData.data["paginated_relating_types"] = AuthoringData.paginated_relating_types() + bpy.ops.bim.load_type_thumbnails(ifc_class=self.ifc_class, offset=9 * (self.type_page - 1), limit=9) + self["type_page"] = min(self["type_page"], AuthoringData.data["total_pages"]) + self["type_page"] = max(self["type_page"], 1) def update_relating_array_from_object(self, context): @@ -101,7 +105,7 @@ def update_search_name(self, context): AuthoringData.load() # Total number of pages may decrease when using the search bar : if self.type_page > AuthoringData.data["total_pages"]: - self.type_page = AuthoringData.data["total_pages"] + self.type_page = max(1, AuthoringData.data["total_pages"]) bpy.ops.bim.load_type_thumbnails(ifc_class=self.ifc_class) @@ -177,7 +181,7 @@ class BIMModelProperties(PropertyGroup): # Used for plan calculation points such as in room generation rl3: bpy.props.FloatProperty(name="RL", default=1, subtype="DISTANCE", description="Z offset for space calculation") x_angle: bpy.props.FloatProperty(name="X Angle", default=0, subtype="ANGLE", min=-pi / 180 * 89, max=pi / 180 * 89) - type_page: bpy.props.IntProperty(name="Type Page", default=1, update=update_type_page) + type_page: bpy.props.IntProperty(name="Type Page", default=1, min=1,update=update_type_page) type_name: bpy.props.StringProperty(name="Name", default="TYPEX") boundary_class: bpy.props.EnumProperty(items=get_boundary_class, name="Boundary Class") direction_sense: bpy.props.EnumProperty( diff --git a/src/bonsai/bonsai/bim/module/model/ui.py b/src/bonsai/bonsai/bim/module/model/ui.py index 08fc46cb6f..0534aa6b55 100644 --- a/src/bonsai/bonsai/bim/module/model/ui.py +++ b/src/bonsai/bonsai/bim/module/model/ui.py @@ -87,7 +87,6 @@ class LaunchTypeManager(bpy.types.Operator): bl_description = "Display all available Construction Types to add new occurrences" def execute(self, context): - bpy.ops.bim.hotkey("INVOKE_DEFAULT", hotkey="S_A") return {"FINISHED"} def invoke(self, context, event): @@ -101,9 +100,7 @@ class LaunchTypeManager(bpy.types.Operator): # will be None if project has no types if ifc_class is not None: bpy.ops.bim.load_type_thumbnails(ifc_class=ifc_class, offset=0, limit=9) - return context.window_manager.invoke_props_dialog( - self, width=550, title="Type Manager", confirm_text="Add Occurrence" - ) + return context.window_manager.invoke_props_dialog(self, width=550, title="Type Manager", confirm_text="Close") def draw(self, context): props = context.scene.BIMModelProperties @@ -113,25 +110,33 @@ class LaunchTypeManager(bpy.types.Operator): text += "s" row.label(text=text, icon="FILE_VOLUME") row.menu("BIM_MT_type_manager_menu", text="", icon="PREFERENCES") + row = self.layout.row(align=True) row.operator("bim.launch_add_element", text=f"Create New {AuthoringData.data['ifc_element_type']}", icon="ADD") + row = self.layout.row(align=True) + row.prop(props, "relating_type_id", text="") + # prop_with_search(row, props, "relating_type_id", text="", should_click_ok_to_validate=True) + row.prop(props, "search_name", icon="FILTER", text="") + columns = self.layout.column_flow(columns=3) - row = columns.row(align=True) - row.alignment = "CENTER" + if AuthoringData.data["total_pages"] > 0: + row = columns.row(align=True) + row.alignment = "RIGHT" + row2 = row.row(align=True) + row2.label(text="Page") + row2.prop(props, "type_page", text="", emboss=False) + row2.label(text=f"/{AuthoringData.data['total_pages']} ") - row = columns.row(align=True) - row.alignment = "RIGHT" - if AuthoringData.data["total_pages"] > 1: - row.label(text=f"Page {props.type_page}/{AuthoringData.data['total_pages']} ") - if AuthoringData.data["prev_page"]: - op = row.operator("bim.change_type_page", icon="TRIA_LEFT", text="") - op.page = AuthoringData.data["prev_page"] - if AuthoringData.data["next_page"]: - op = row.operator("bim.change_type_page", icon="TRIA_RIGHT", text="") - op.page = AuthoringData.data["next_page"] + prev_page_op = row.row(align=True) + op = prev_page_op.operator("bim.change_type_page", icon="TRIA_LEFT", text="") + op.page = AuthoringData.data["prev_page"] or 0 + prev_page_op.enabled = op.page > 0 - self.layout.prop(props, "search_name", icon="VIEWZOOM", text="") + next_page_op = row.row(align=True) + op = next_page_op.operator("bim.change_type_page", icon="TRIA_RIGHT", text="") + op.page = AuthoringData.data["next_page"] or 0 + next_page_op.enabled = op.page > 0 flow = self.layout.grid_flow(row_major=True, columns=3, even_columns=True, even_rows=True, align=True) @@ -139,11 +144,11 @@ class LaunchTypeManager(bpy.types.Operator): outer_col = flow.column() box = outer_col.box() - row = box.row(align=True) + row = box.row() op = row.operator("bim.set_active_type", text=relating_type["name"], icon="BLANK1", emboss=False) op.relating_type = relating_type["id"] - op = row.operator("bim.launch_type_menu", icon="PREFERENCES", text="", emboss=False) + op = row.operator("bim.launch_type_menu", icon="PREFERENCES", text="", emboss=True) op.relating_type_id = relating_type["id"] row = box.row() diff --git a/src/bonsai/bonsai/bim/module/model/workspace.py b/src/bonsai/bonsai/bim/module/model/workspace.py index ecdf6d6044..0e6e3bf4e9 100644 --- a/src/bonsai/bonsai/bim/module/model/workspace.py +++ b/src/bonsai/bonsai/bim/module/model/workspace.py @@ -509,13 +509,9 @@ class CreateObjectUI: if ifc_class: box = cls.layout.box() row = box.row(align=True) - prop_with_search(row, cls.props, "relating_type_id", text="") - if AuthoringData.data["type_thumbnail"] and ui_context == "TOOL_HEADER": - row.template_icon(icon_value=AuthoringData.data["type_thumbnail"]) - row.operator("bim.launch_type_manager", text="Type Manager", emboss=False) - else: - row.operator("bim.launch_type_manager", icon="BLANK1", text="Type Manager", emboss=False) - + if ui_context == "TOOL_HEADER": + row.template_icon(icon_value=AuthoringData.data.get("type_thumbnail", 0)) + row.operator("bim.launch_type_manager", text=AuthoringData.data["relating_type_name"], emboss=False) row.operator( "bim.launch_type_manager", icon=tool.Blender.TYPE_MANAGER_ICON,