From 9c39ac0a489658dde27e13293246d3d1913097b9 Mon Sep 17 00:00:00 2001 From: Gorgious Date: Sat, 22 Nov 2025 18:12:50 +0100 Subject: [PATCH] You can now edit default parameters when generating a new parametric geometry for Door, Window, Railing, Roof and Stair types. UI is in the addon preferences. --- src/bonsai/bonsai/bim/__init__.py | 37 ++- src/bonsai/bonsai/bim/module/model/door.py | 9 +- src/bonsai/bonsai/bim/module/model/prop.py | 35 +++ src/bonsai/bonsai/bim/module/model/railing.py | 2 + src/bonsai/bonsai/bim/module/model/roof.py | 2 + src/bonsai/bonsai/bim/module/model/stair.py | 2 + src/bonsai/bonsai/bim/module/model/ui.py | 220 ++++++++++-------- src/bonsai/bonsai/bim/module/model/window.py | 5 + src/bonsai/bonsai/bim/ui.py | 67 ++++++ 9 files changed, 277 insertions(+), 102 deletions(-) diff --git a/src/bonsai/bonsai/bim/__init__.py b/src/bonsai/bonsai/bim/__init__.py index 79fc5fdd54..68bb5773c5 100644 --- a/src/bonsai/bonsai/bim/__init__.py +++ b/src/bonsai/bonsai/bim/__init__.py @@ -148,7 +148,7 @@ classes = [ ui.BIM_UL_clipping_plane, ui.BIM_UL_generic, ui.DocPreferences, - ui.BIM_ADDON_preferences, + # ui.DefaultParameters and ui.BIM_ADDON_preferences are registered separately after modules (see late_classes below) # Tabs panel ui.BIM_PT_tabs, # Project overview @@ -225,14 +225,33 @@ def on_register(scene): is_registering = False -def register(): - for cls in classes: +# Classes that need to be registered after modules (due to cross-module dependencies) +late_classes = ( + ui.DefaultParameters, # Register before BIM_ADDON_preferences + ui.BIM_ADDON_preferences, +) + + +def register_classes(classes_to_register): + for cls in classes_to_register: # Prevent crashes in Blender 4.4.0, see #6420. if issubclass(cls, (ImportHelper, ExportHelper)): assert getattr(cls, "bl_description", "") or cls.__doc__, cls bpy.utils.register_class(cls) + +def unregister_classes(classes_to_unregister): + for cls in reversed(classes_to_unregister): + if getattr(cls, "is_registered", None) is None: + bpy.utils.unregister_class(cls) + elif cls.is_registered: + bpy.utils.unregister_class(cls) + + +def register(): + register_classes(classes) + bpy.app.handlers.depsgraph_update_post.append(on_register) bpy.app.handlers.undo_post.append(handler.undo_post) bpy.app.handlers.redo_post.append(handler.redo_post) @@ -256,6 +275,9 @@ def register(): for mod in modules.values(): mod.register() + # Delay registering classes that depend on module classes + register_classes(late_classes) + wm = bpy.context.window_manager if wm.keyconfigs.addon: km = wm.keyconfigs.addon.keymaps.new(name="Window", space_type="EMPTY") @@ -287,11 +309,7 @@ def unregister(): bpy.utils.previews.remove(icons) - for cls in reversed(classes): - if getattr(cls, "is_registered", None) is None: - bpy.utils.unregister_class(cls) - elif cls.is_registered: - bpy.utils.unregister_class(cls) + unregister_classes(classes) bpy.app.handlers.load_post.remove(handler.load_post) bpy.app.handlers.load_post.remove(handler.loadIfcStore) @@ -306,6 +324,9 @@ def unregister(): bpy.types.UI_MT_button_context_menu.remove(ui.draw_custom_context_menu) bpy.types.STATUSBAR_HT_header.remove(ui.draw_statusbar) + # Unregister late classes before modules + unregister_classes(late_classes) + for mod in reversed(list(modules.values())): mod.unregister() diff --git a/src/bonsai/bonsai/bim/module/model/door.py b/src/bonsai/bonsai/bim/module/model/door.py index abd96f7319..d73a16deff 100644 --- a/src/bonsai/bonsai/bim/module/model/door.py +++ b/src/bonsai/bonsai/bim/module/model/door.py @@ -291,6 +291,8 @@ def update_door_modifier_bmesh(context: bpy.types.Context) -> None: obj = context.active_object assert obj props = tool.Model.get_door_props(obj) + if not props.is_editing: + return overall_width = props.overall_width overall_height = props.overall_height @@ -408,7 +410,10 @@ def update_door_modifier_bmesh(context: bpy.types.Context) -> None: if door_swing_type == "LEFT": bm_mirror( - bm, door_handle_verts, mirror_axes=V_(1, 0, 0), mirror_point=panel_position + V_(panel_size.x / 2, 0, 0) + bm, + door_handle_verts, + mirror_axes=V_(1, 0, 0), + mirror_point=panel_position + V_(panel_size.x / 2, 0, 0), ) door_handle_mirrored_verts = bm_mirror( @@ -528,6 +533,8 @@ class AddDoor(bpy.types.Operator, tool.Ifc.Operator): assert element props = tool.Model.get_door_props(obj) + tool.Blender.get_addon_preferences().default_parameters.door.copy_to(props) + door_data = props.get_general_kwargs(convert_to_project_units=True) lining_props = props.get_lining_kwargs(convert_to_project_units=True) panel_props = props.get_panel_kwargs(convert_to_project_units=True) diff --git a/src/bonsai/bonsai/bim/module/model/prop.py b/src/bonsai/bonsai/bim/module/model/prop.py index 083ad4ba39..dc9285114e 100644 --- a/src/bonsai/bonsai/bim/module/model/prop.py +++ b/src/bonsai/bonsai/bim/module/model/prop.py @@ -645,6 +645,15 @@ class BIMStairProperties(PropertyGroup): for prop_name in kwargs: setattr(self, prop_name, kwargs[prop_name]) + def copy_to(self, target_props: "BIMStairProperties") -> None: + """Copy preset values to target stair properties.""" + target_props.custom_tread_lock = self.custom_tread_lock + for prop_name, prop_value in self.get_props_kwargs().items(): + # Skip custom_first_last_tread_run if it contains None values (when lock is enabled) + if prop_name == "custom_first_last_tread_run" and None in prop_value: + continue + setattr(target_props, prop_name, prop_value) + class BIMSverchokProperties(PropertyGroup): node_group: bpy.props.PointerProperty(name="Node Group", type=NodeTree) @@ -863,6 +872,14 @@ class BIMWindowProperties(PropertyGroup): for prop_name in kwargs: setattr(self, prop_name, kwargs[prop_name]) + def copy_to(self, target_props: "BIMWindowProperties") -> None: + """Copy preset values to target window properties, excluding materials.""" + exclude_props = {"lining_material", "framing_material", "glazing_material"} + for kwargs_dict in (self.get_general_kwargs(), self.get_lining_kwargs(), self.get_panel_kwargs()): + for prop_name, prop_value in kwargs_dict.items(): + if prop_name not in exclude_props: + setattr(target_props, prop_name, prop_value) + DoorType = Literal[ "SINGLE_SWING_LEFT", @@ -1130,6 +1147,14 @@ class BIMDoorProperties(PropertyGroup): for prop_name in kwargs: setattr(self, prop_name, kwargs[prop_name]) + def copy_to(self, target_props: "BIMDoorProperties") -> None: + """Copy properties to target door properties, excluding materials.""" + exclude_props = {"lining_material", "framing_material", "glazing_material"} + for kwargs_dict in (self.get_general_kwargs(), self.get_lining_kwargs(), self.get_panel_kwargs()): + for prop_name, prop_value in kwargs_dict.items(): + if prop_name not in exclude_props: + setattr(target_props, prop_name, prop_value) + RailingType = Literal["FRAMELESS_PANEL", "WALL_MOUNTED_HANDRAIL"] CapType = Literal["TO_END_POST_AND_FLOOR", "TO_END_POST", "TO_FLOOR", "TO_WALL", "180", "NONE"] @@ -1229,6 +1254,11 @@ class BIMRailingProperties(PropertyGroup): for prop_name in kwargs: setattr(self, prop_name, kwargs[prop_name]) + def copy_to(self, target_props: "BIMRailingProperties") -> None: + """Copy preset values to target railing properties.""" + for prop_name, prop_value in self.get_general_kwargs().items(): + setattr(target_props, prop_name, prop_value) + def to_angle(percentage: float) -> float: return math.atan(percentage / 100) @@ -1331,6 +1361,11 @@ class BIMRoofProperties(PropertyGroup): for prop_name in kwargs: setattr(self, prop_name, kwargs[prop_name]) + def copy_to(self, target_props: "BIMRoofProperties") -> None: + """Copy preset values to target roof properties.""" + for prop_name, prop_value in self.get_general_kwargs().items(): + setattr(target_props, prop_name, prop_value) + class SnapMousePoint(PropertyGroup): x: bpy.props.FloatProperty(name="X") diff --git a/src/bonsai/bonsai/bim/module/model/railing.py b/src/bonsai/bonsai/bim/module/model/railing.py index 7939758c2b..4f6fc348f0 100644 --- a/src/bonsai/bonsai/bim/module/model/railing.py +++ b/src/bonsai/bonsai/bim/module/model/railing.py @@ -342,6 +342,8 @@ class AddRailing(bpy.types.Operator, tool.Ifc.Operator): props = tool.Model.get_railing_props(obj) si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + tool.Blender.get_addon_preferences().default_parameters.railing.copy_to(props) + railing_data = props.get_general_kwargs(convert_to_project_units=True) path_data = get_path_data(obj) diff --git a/src/bonsai/bonsai/bim/module/model/roof.py b/src/bonsai/bonsai/bim/module/model/roof.py index dfcf4ebbf1..daf41b86dd 100644 --- a/src/bonsai/bonsai/bim/module/model/roof.py +++ b/src/bonsai/bonsai/bim/module/model/roof.py @@ -570,6 +570,8 @@ class AddRoof(bpy.types.Operator, tool.Ifc.Operator): props = tool.Model.get_roof_props(obj) si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + tool.Blender.get_addon_preferences().default_parameters.roof.copy_to(props) + # rejecting original roof shape to be safe # taking into account only it's bounding box dimensions if obj.dimensions.x == 0 or obj.dimensions.y == 0: diff --git a/src/bonsai/bonsai/bim/module/model/stair.py b/src/bonsai/bonsai/bim/module/model/stair.py index b359153f20..38480089e3 100644 --- a/src/bonsai/bonsai/bim/module/model/stair.py +++ b/src/bonsai/bonsai/bim/module/model/stair.py @@ -188,6 +188,8 @@ class AddStair(bpy.types.Operator, tool.Ifc.Operator): props = tool.Model.get_stair_props(obj) ifc_file = tool.Ifc.get() + tool.Blender.get_addon_preferences().default_parameters.stair.copy_to(props) + # Use the special method that includes custom_tread_lock for IFC storage stair_data = props.get_props_kwargs_for_ifc_export(convert_to_project_units=True) pset = tool.Pset.get_element_pset(element, "BBIM_Stair") diff --git a/src/bonsai/bonsai/bim/module/model/ui.py b/src/bonsai/bonsai/bim/module/model/ui.py index 535d6f0ad3..5d89e7bfee 100644 --- a/src/bonsai/bonsai/bim/module/model/ui.py +++ b/src/bonsai/bonsai/bim/module/model/ui.py @@ -305,48 +305,9 @@ class BIM_PT_stair(bpy.types.Panel): row.operator("bim.finish_editing_stair", icon="CHECKMARK", text="Finish Editing") row.operator("bim.cancel_editing_stair", icon="CANCEL", text="") row = self.layout.row(align=True) - for prop_name in props.get_props_kwargs(): - # Skip custom_tread_lock as it's handled with custom_first_last_tread_run - if prop_name == "custom_tread_lock": - continue - prop_value = getattr(props, prop_name) + draw_stair_properties(self.layout, props) - # Special handling for custom_first_last_tread_run - if prop_name == "custom_first_last_tread_run": - # Draw the lock toggle - row_lock = self.layout.row(align=True) - lock_text = ( - "Lock First/Last Treads" if not props.custom_tread_lock else "Unlock First/Last Treads" - ) - row_lock.prop( - props, - "custom_tread_lock", - text=lock_text, - icon="LOCKED" if props.custom_tread_lock else "UNLOCKED", - ) - - # Only show the custom values input if unlocked - if not props.custom_tread_lock: - prop_readable_name = props.bl_rna.properties[prop_name].name - self.layout.label(text=f"{prop_readable_name}:") - self.layout.prop(props, prop_name, text="") - elif isinstance(prop_value, Iterable) and not isinstance(prop_value, str): - prop_readable_name = props.bl_rna.properties[prop_name].name - self.layout.label(text=f"{prop_readable_name}:") - self.layout.prop(props, prop_name, text="") - else: - self.layout.prop(props, prop_name) - - if prop_name == "height": # Weak but we just want to insert this inside props drawing - row_length = self.layout.row(align=True) - row_length.prop(props, "total_length_target") - row_length.prop( - props, - "total_length_lock", - text="", - icon="LOCKED" if props.total_length_lock else "UNLOCKED", - ) regenerate_stair_mesh(obj) else: calculated_params = StairData.data["calculated_params"] @@ -440,39 +401,11 @@ class BIM_PT_window(bpy.types.Panel): row.label(text="Window parameters", icon="OUTLINER_OB_LATTICE") if props.is_editing: - number_of_panels, panels_data = props.window_types_panels[props.window_type] row = self.layout.row(align=True) row.operator("bim.finish_editing_window", icon="CHECKMARK", text="Finish Editing") row.operator("bim.cancel_editing_window", icon="CANCEL", text="") - general_props = props.get_general_kwargs() - for prop in general_props: - self.layout.prop(props, prop) - - lining_props = props.get_lining_kwargs() - self.layout.label(text="Lining properties") - for prop in lining_props: - self.layout.prop(props, prop) - - panel_props = props.get_panel_kwargs() - self.layout.label(text="Panel properties") - - panel_box = self.layout.box() - row = panel_box.row() - cols = [row.column(align=True) for i in range(number_of_panels + 1)] - - cols[0].label(text="") - - for panel_i in range(number_of_panels): - r = cols[panel_i + 1].row() - r.alignment = "CENTER" - r.label(text=f"#{panel_i}") - r = cols[panel_i + 1].row() - - for prop in panel_props: - cols[0].label(text=f"{props.bl_rna.properties[prop].name}") - for panel_i in range(number_of_panels): - cols[panel_i + 1].prop(props, prop, index=panel_i, text="") + draw_window_properties(self.layout, props) self.layout.use_property_split = True self.layout.label(text="Material Properties") @@ -560,19 +493,7 @@ class BIM_PT_door(bpy.types.Panel): row.operator("bim.finish_editing_door", icon="CHECKMARK", text="Finish Editing") row.operator("bim.cancel_editing_door", icon="CANCEL", text="") - general_props = props.get_general_kwargs() - for prop in general_props: - self.layout.prop(props, prop) - - lining_props = props.get_lining_kwargs() - self.layout.label(text="Lining Properties") - for prop in lining_props: - self.layout.prop(props, prop) - - panel_props = props.get_panel_kwargs() - self.layout.label(text="Panel Properties") - for prop in panel_props: - self.layout.prop(props, prop) + draw_door_properties(self.layout, props) self.layout.use_property_split = True self.layout.label(text="Material Properties") @@ -643,14 +564,7 @@ class BIM_PT_railing(bpy.types.Panel): row.operator("bim.finish_editing_railing", icon="CHECKMARK", text="Finish Editing") row.operator("bim.cancel_editing_railing", icon="CANCEL", text="") - general_props = props.get_general_kwargs() - for prop in general_props: - if prop == "support_spacing" and props.use_manual_supports: - row = self.layout.row() - row.prop(props, prop) - row.active = False - continue - self.layout.prop(props, prop) + draw_railing_properties(self.layout, props) update_railing_modifier_bmesh(context) @@ -708,9 +622,7 @@ class BIM_PT_roof(bpy.types.Panel): row.operator("bim.finish_editing_roof", icon="CHECKMARK", text="Finish Editing") row.operator("bim.cancel_editing_roof", icon="CANCEL", text="") - general_props = props.get_general_kwargs() - for prop in general_props: - self.layout.prop(props, prop) + draw_roof_properties(self.layout, props) update_roof_modifier_bmesh(obj) elif props.is_editing_path: @@ -776,6 +688,128 @@ class BIM_PT_external_parametric_geometry(bpy.types.Panel): row.prop(input, "default_value", text=input.name) +def draw_door_properties(layout: bpy.types.UILayout, props: bpy.types.PropertyGroup) -> None: + """Draw door properties UI (shared between properties panel and preferences).""" + # General properties + general_props = props.get_general_kwargs() + for prop in general_props: + layout.prop(props, prop) + + # Lining properties + layout.label(text="Lining Properties") + lining_props = props.get_lining_kwargs() + for prop in lining_props: + layout.prop(props, prop) + + # Panel properties + layout.label(text="Panel Properties") + panel_props = props.get_panel_kwargs() + for prop in panel_props: + layout.prop(props, prop) + + +def draw_window_properties(layout: bpy.types.UILayout, props: bpy.types.PropertyGroup) -> None: + """Draw window properties UI (shared between properties panel and preferences).""" + number_of_panels, panels_data = props.window_types_panels[props.window_type] + + # General and lining properties + general_props = props.get_general_kwargs() + for prop in general_props: + layout.prop(props, prop) + + layout.label(text="Lining Properties") + lining_props = props.get_lining_kwargs() + for prop in lining_props: + layout.prop(props, prop) + + # Panel properties (special layout for multiple panels) + panel_props = props.get_panel_kwargs() + layout.label(text="Panel Properties") + + panel_box = layout.box() + row = panel_box.row() + cols = [row.column(align=True) for i in range(number_of_panels + 1)] + + cols[0].label(text="") + + for panel_i in range(number_of_panels): + r = cols[panel_i + 1].row() + r.alignment = "CENTER" + r.label(text=f"#{panel_i}") + r = cols[panel_i + 1].row() + + for prop in panel_props: + cols[0].label(text=f"{props.bl_rna.properties[prop].name}") + for panel_i in range(number_of_panels): + cols[panel_i + 1].prop(props, prop, index=panel_i, text="") + + +def draw_railing_properties(layout: bpy.types.UILayout, props: bpy.types.PropertyGroup) -> None: + """Draw railing properties UI (shared between properties panel and preferences).""" + general_props = props.get_general_kwargs() + for prop in general_props: + if prop == "support_spacing" and props.use_manual_supports: + row = layout.row() + row.prop(props, prop) + row.active = False + continue + layout.prop(props, prop) + + +def draw_roof_properties(layout: bpy.types.UILayout, props: bpy.types.PropertyGroup) -> None: + """Draw roof properties UI (shared between properties panel and preferences).""" + # General properties + general_props = props.get_general_kwargs() + for prop in general_props: + layout.prop(props, prop) + + +def draw_stair_properties(layout: bpy.types.UILayout, props: bpy.types.PropertyGroup) -> None: + """Draw stair properties UI (shared between properties panel and preferences).""" + for prop_name in props.get_props_kwargs(): + # Skip custom_tread_lock as it's handled with custom_first_last_tread_run + if prop_name == "custom_tread_lock": + continue + + prop_value = getattr(props, prop_name) + + # Special handling for custom_first_last_tread_run + if prop_name == "custom_first_last_tread_run": + # Draw the lock toggle + row_lock = layout.row(align=True) + lock_text = ( + "Lock First/Last Treads" if not props.custom_tread_lock else "Unlock First/Last Treads" + ) + row_lock.prop( + props, + "custom_tread_lock", + text=lock_text, + icon="LOCKED" if props.custom_tread_lock else "UNLOCKED", + ) + + # Only show the custom values input if unlocked + if not props.custom_tread_lock: + prop_readable_name = props.bl_rna.properties[prop_name].name + layout.label(text=f"{prop_readable_name}:") + layout.prop(props, prop_name, text="") + elif isinstance(prop_value, Iterable) and not isinstance(prop_value, str): + prop_readable_name = props.bl_rna.properties[prop_name].name + layout.label(text=f"{prop_readable_name}:") + layout.prop(props, prop_name, text="") + else: + layout.prop(props, prop_name) + + if prop_name == "height": # Weak but we just want to insert this inside props drawing + row_length = layout.row(align=True) + row_length.prop(props, "total_length_target") + row_length.prop( + props, + "total_length_lock", + text="", + icon="LOCKED" if props.total_length_lock else "UNLOCKED", + ) + + def add_menu(self: bpy.types.Menu, context: bpy.types.Context) -> None: self.layout.operator_context = "INVOKE_REGION_WIN" self.layout.operator("bim.add_element", icon_value=bonsai.bim.icons["IFC"].icon_id, text="IFC Element") diff --git a/src/bonsai/bonsai/bim/module/model/window.py b/src/bonsai/bonsai/bim/module/model/window.py index 1aebc8cd0b..c166ff7d54 100644 --- a/src/bonsai/bonsai/bim/module/model/window.py +++ b/src/bonsai/bonsai/bim/module/model/window.py @@ -258,6 +258,9 @@ def update_window_modifier_bmesh(context: bpy.types.Context) -> None: obj = context.active_object assert obj props = tool.Model.get_window_props(obj) + if not props.is_editing: + return + panel_schema = DEFAULT_PANEL_SCHEMAS[props.window_type] accumulated_height = [0] * len(panel_schema[0]) built_panels = [] @@ -451,6 +454,8 @@ class AddWindow(bpy.types.Operator, tool.Ifc.Operator): assert element props = tool.Model.get_window_props(obj) + tool.Blender.get_addon_preferences().default_parameters.window.copy_to(props) + window_data = props.get_general_kwargs(convert_to_project_units=True) lining_props = props.get_lining_kwargs(convert_to_project_units=True) panel_props = props.get_panel_kwargs(convert_to_project_units=True) diff --git a/src/bonsai/bonsai/bim/ui.py b/src/bonsai/bonsai/bim/ui.py index 25137cf639..e99ed4bb5d 100644 --- a/src/bonsai/bonsai/bim/ui.py +++ b/src/bonsai/bonsai/bim/ui.py @@ -38,6 +38,20 @@ from ifcopenshell.util.file import IfcHeaderExtractor from bonsai.bim.prop import Attribute from bonsai.bim.module.bsdd.prop import BIMBSDDProperties, BSDDProperty from bonsai.bim.module.pset.prop import IfcProperty +from bonsai.bim.module.model.prop import ( + BIMDoorProperties, + BIMWindowProperties, + BIMRailingProperties, + BIMRoofProperties, + BIMStairProperties, +) +from bonsai.bim.module.model.ui import ( + draw_door_properties, + draw_window_properties, + draw_railing_properties, + draw_roof_properties, + draw_stair_properties, +) from typing import Optional, TYPE_CHECKING, Literal from natsort import natsorted import textwrap @@ -320,6 +334,14 @@ class DocPreferences(bpy.types.PropertyGroup): classes_no_cut: str +class DefaultParameters(bpy.types.PropertyGroup): + door: bpy.props.PointerProperty(type=BIMDoorProperties) + window: bpy.props.PointerProperty(type=BIMWindowProperties) + railing: bpy.props.PointerProperty(type=BIMRailingProperties) + roof: bpy.props.PointerProperty(type=BIMRoofProperties) + stair: bpy.props.PointerProperty(type=BIMStairProperties) + + class BIM_ADDON_preferences(bpy.types.AddonPreferences): bl_idname = tool.Blender.get_blender_addon_package_name() svg2pdf_command: StringProperty( @@ -523,6 +545,11 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): subtype="DIR_PATH", ) doc: bpy.props.PointerProperty(type=DocPreferences) + default_parameters: bpy.props.PointerProperty( + type=DefaultParameters, + name="Default Parameters", + description="Default parameters for BIM elements", + ) if TYPE_CHECKING: svg2pdf_command: str @@ -557,6 +584,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): cache_dir: str pset_dir: str doc: DocPreferences + default_parameters: DefaultParameters def draw(self, context: bpy.types.Context) -> None: layout = self.layout @@ -581,6 +609,12 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): bonsai.bim.helper.draw_expandable_panel(self.layout, context, "Colors", self.draw_decorator_colors) bonsai.bim.helper.draw_expandable_panel(self.layout, context, "Directories", self.draw_directories) bonsai.bim.helper.draw_expandable_panel(self.layout, context, "Drawing", self.draw_drawing_settings) + bonsai.bim.helper.draw_expandable_panel( + self.layout, + context, + "Default Parameters", + self.draw_default_parameters, + ) bonsai.bim.helper.draw_expandable_panel(self.layout, context, "Other", self.draw_other_settings) def draw_commands(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None: @@ -639,6 +673,39 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): layout.row().prop(self, "decorator_color_error") layout.row().prop(self, "decorator_color_background") + def draw_default_parameters(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None: + box = layout.box() + bonsai.bim.helper.draw_expandable_panel( + box, + context, + "Door", + lambda _layout, _context: draw_door_properties(_layout, self.default_parameters.door), + ) + bonsai.bim.helper.draw_expandable_panel( + box, + context, + "Window", + lambda _layout, _context: draw_window_properties(_layout, self.default_parameters.window), + ) + bonsai.bim.helper.draw_expandable_panel( + box, + context, + "Railing", + lambda _layout, _context: draw_railing_properties(_layout, self.default_parameters.railing), + ) + bonsai.bim.helper.draw_expandable_panel( + box, + context, + "Roof", + lambda _layout, _context: draw_roof_properties(_layout, self.default_parameters.roof), + ) + bonsai.bim.helper.draw_expandable_panel( + box, + context, + "Stair", + lambda _layout, _context: draw_stair_properties(_layout, self.default_parameters.stair), + ) + def draw_other_settings(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None: layout.prop(self, "opening_focus_opacity") layout.prop(self, "should_disable_undo_on_save")