From 9093c9d41954eb7c6da2dd9999e080bf9427d684 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 8 Jan 2024 16:03:34 +1100 Subject: [PATCH] Fix #4176. Bug where tabbed interface didn't work on new or temporary screens. --- src/blenderbim/blenderbim/bim/__init__.py | 2 ++ src/blenderbim/blenderbim/bim/handler.py | 8 +---- src/blenderbim/blenderbim/bim/operator.py | 6 ++-- src/blenderbim/blenderbim/bim/prop.py | 18 ++++++++++ src/blenderbim/blenderbim/bim/ui.py | 12 +++++-- src/blenderbim/blenderbim/tool/blender.py | 41 ++++++++++++++++------- 6 files changed, 63 insertions(+), 24 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/__init__.py b/src/blenderbim/blenderbim/bim/__init__.py index e6e97ac378..956830ffe8 100644 --- a/src/blenderbim/blenderbim/bim/__init__.py +++ b/src/blenderbim/blenderbim/bim/__init__.py @@ -115,6 +115,7 @@ classes = [ prop.ObjProperty, prop.Attribute, prop.BIMAreaProperties, + prop.BIMTabProperties, prop.BIMProperties, prop.IfcParameter, prop.PsetQto, @@ -206,6 +207,7 @@ def register(): bpy.app.handlers.load_post.append(handler.loadIfcStore) bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties) bpy.types.Screen.BIMAreaProperties = bpy.props.CollectionProperty(type=prop.BIMAreaProperties) + bpy.types.Screen.BIMTabProperties = bpy.props.PointerProperty(type=prop.BIMTabProperties) bpy.types.Collection.BIMCollectionProperties = bpy.props.PointerProperty(type=prop.BIMCollectionProperties) bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) diff --git a/src/blenderbim/blenderbim/bim/handler.py b/src/blenderbim/blenderbim/bim/handler.py index 6dc8ead3d1..bf07cc9fc6 100644 --- a/src/blenderbim/blenderbim/bim/handler.py +++ b/src/blenderbim/blenderbim/bim/handler.py @@ -354,10 +354,4 @@ def load_post(scene): bpy.utils.register_class(override_panel) bpy.utils.unregister_class(original_panel) blenderbim.bim.overridden_scene_panels[panel] = (original_panel, override_panel) - # https://blender.stackexchange.com/questions/140644/how-can-make-the-state-of-a-boolean-property-relative-to-the-3d-view-area - for screen in bpy.data.screens: - if len(screen.BIMAreaProperties) == 20: - continue - screen.BIMAreaProperties.clear() - for i in range(20): # 20 is an arbitrary value of split areas - screen.BIMAreaProperties.add() + tool.Blender.setup_tabs() diff --git a/src/blenderbim/blenderbim/bim/operator.py b/src/blenderbim/blenderbim/bim/operator.py index 69f46e5f34..8a47308b43 100644 --- a/src/blenderbim/blenderbim/bim/operator.py +++ b/src/blenderbim/blenderbim/bim/operator.py @@ -55,7 +55,8 @@ class SetTab(bpy.types.Operator): def execute(self, context): if context.area.spaces.active.search_filter: return {"FINISHED"} - aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)] + tool.Blender.setup_tabs() + aprops = tool.Blender.get_area_props(context) aprops.tab = self.tab return {"FINISHED"} @@ -69,7 +70,8 @@ class SwitchTab(bpy.types.Operator): def execute(self, context): if context.area.spaces.active.search_filter: return {"FINISHED"} - aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)] + tool.Blender.setup_tabs() + aprops = tool.Blender.get_area_props(context) aprops.tab = aprops.alt_tab return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/prop.py b/src/blenderbim/blenderbim/bim/prop.py index 41cd9b0a2a..c35aac8ea6 100644 --- a/src/blenderbim/blenderbim/bim/prop.py +++ b/src/blenderbim/blenderbim/bim/prop.py @@ -58,6 +58,13 @@ def update_tab(self, context): self.previous_tab = self.tab +def update_global_tab(self, context): + tool.Blender.setup_tabs() + screen = tool.Blender.get_screen(context) + aprops = screen.BIMAreaProperties[screen.areas[:].index(context.area)] + aprops.tab = self.tab + + # If we don't cache strings, accents get mangled due to a Blender bug # https://blender.stackexchange.com/questions/216230/is-there-a-workaround-for-the-known-bug-in-dynamic-enumproperty # https://github.com/IfcOpenShell/IfcOpenShell/pull/1945 @@ -332,6 +339,17 @@ class BIMAreaProperties(PropertyGroup): inactive_tab: BoolProperty(default=False, name="Inactive Tab") +# BIMAreaProperties exists per area and is setup on load post. However, for new +# or temporary screens, they may not be setup yet, so this global tab +# properties is used as a fallback. +class BIMTabProperties(PropertyGroup): + tab: EnumProperty(default=0, items=get_tab, name="Tab", update=update_global_tab) + previous_tab: StringProperty(default="PROJECT", name="Previous Tab") + alt_tab: StringProperty(default="OBJECT", name="Alt Tab") + active_tab: BoolProperty(default=True, name="Active Tab") + inactive_tab: BoolProperty(default=False, name="Inactive Tab") + + class BIMProperties(PropertyGroup): schema_dir: StringProperty( default=os.path.join(cwd, "schema") + os.path.sep, name="Schema Directory", update=update_schema_dir diff --git a/src/blenderbim/blenderbim/bim/ui.py b/src/blenderbim/blenderbim/bim/ui.py index ee896d24dc..b04b67e425 100644 --- a/src/blenderbim/blenderbim/bim/ui.py +++ b/src/blenderbim/blenderbim/bim/ui.py @@ -138,7 +138,11 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): openlca_port: IntProperty(name="OpenLCA IPC Port", default=8080) should_hide_empty_props: BoolProperty(name="Should Hide Empty Properties", default=True) should_setup_workspace: BoolProperty(name="Should Setup Workspace Layout for BIM", default=True) - should_setup_toolbar: BoolProperty(name="Always Show Toolbar In 3D Viewport", default=True, description="If disabled, the toolbar will only load when an IFC model is active") + should_setup_toolbar: BoolProperty( + name="Always Show Toolbar In 3D Viewport", + default=True, + description="If disabled, the toolbar will only load when an IFC model is active", + ) should_play_chaching_sound: BoolProperty( name="Should Make A Cha-Ching Sound When Project Costs Updates", default=False ) @@ -288,7 +292,10 @@ class BIM_PT_tabs(Panel): def draw(self, context): try: is_ifc_project = bool(tool.Ifc.get()) - aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)] + aprops = tool.Blender.get_area_props(context) + if not aprops: + # Fallback in case areas aren't setup yet. + aprops = context.screen.BIMTabProperties row = self.layout.row() row.operator( @@ -330,7 +337,6 @@ class BIM_PT_tabs(Panel): else: row.prop(aprops, "inactive_tab", text="", icon="BLANK1", emboss=False) - aprops = context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)] row = self.layout.row(align=True) row.prop(aprops, "tab", text="") except: diff --git a/src/blenderbim/blenderbim/tool/blender.py b/src/blenderbim/blenderbim/tool/blender.py index 47ee145b76..3332b35c3f 100644 --- a/src/blenderbim/blenderbim/tool/blender.py +++ b/src/blenderbim/blenderbim/tool/blender.py @@ -42,26 +42,41 @@ class Blender(blenderbim.core.tool.Blender): OBJECT_TYPES_THAT_SUPPORT_EDIT_MODE = ("MESH", "CURVE", "SURFACE", "META", "FONT", "LATTICE", "ARMATURE") OBJECT_TYPES_THAT_SUPPORT_EDIT_GPENCIL_MODE = ("GPENCIL",) + @classmethod + def get_area_props(cls, context): + try: + if context.screen.name.endswith("-nonnormal"): # Ctrl-space temporary fullscreen + screen = bpy.data.screens[context.screen.name[0 : -len("-nonnormal")]] + # The original area object has its type changed to "EMPTY" apparently + index = [a.type for a in screen.areas].index("EMPTY") + return screen.BIMAreaProperties[index] + return context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)] + except: + return + @classmethod def set_active_object(cls, obj): bpy.context.view_layer.objects.active = obj obj.select_set(True) + @classmethod + def setup_tabs(cls): + # https://blender.stackexchange.com/questions/140644/how-can-make-the-state-of-a-boolean-property-relative-to-the-3d-view-area + for screen in bpy.data.screens: + if len(screen.BIMAreaProperties) == 20: + continue + screen.BIMAreaProperties.clear() + for i in range(20): # 20 is an arbitrary value of split areas + screen.BIMAreaProperties.add() + @classmethod def is_tab(cls, context, tab): - if not len(context.screen.BIMAreaProperties): - return None + aprops = cls.get_area_props(context) + if not aprops: + return context.screen.BIMTabProperties.tab == tab if context.area.spaces.active.search_filter: return True - screen_areas = context.screen.areas[:] - current_area = context.area - # If the user is using the properties panel "Display Filter" search it - # will create a new area that's not present in context.screen.areas for - # all property tabs except for the active property tab. - if current_area not in screen_areas: - current_area = next(a for a in context.screen.areas if a.x == current_area.x and a.y == current_area.y) - area_index = screen_areas.index(current_area) - return context.screen.BIMAreaProperties[area_index].tab == tab + return aprops.tab == tab @classmethod def is_default_scene(cls): @@ -679,7 +694,9 @@ class Blender(blenderbim.core.tool.Blender): bpy.utils.register_tool( ws_structural.StructuralTool, after={"bim.spatial_tool"}, separator=False, group=False ) - bpy.utils.register_tool(ws_covering.CoveringTool, after={"bim.structural_tool"}, separator=False, group=False) + bpy.utils.register_tool( + ws_covering.CoveringTool, after={"bim.structural_tool"}, separator=False, group=False + ) except: pass