diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py index 8c29bfbf41..4c52993aa0 100644 --- a/src/bonsai/bonsai/bim/module/drawing/operator.py +++ b/src/bonsai/bonsai/bim/module/drawing/operator.py @@ -3185,166 +3185,15 @@ class EditText(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} def _execute(self, context): - obj = context.active_object - props = tool.Drawing.get_text_props(obj) - - captured_apply_settings = { - "apply_font_size_to_all": props.apply_font_size_to_all, - "apply_newline_to_all": props.apply_newline_to_all, - "font_size": props.font_size, - "newline_at": props.newline_at, - "literals": [], - } - - for i, literal in enumerate(props.literals): - literal_data = { - "attributes": [ - (attr.string_value, attr.enum_value if attr.data_type == "enum" else attr.string_value) - for attr in literal.attributes - ], - "box_alignment": literal.box_alignment[:] if hasattr(literal, "box_alignment") else None, - "element_value_rows": [ - { - "category": row.category, - "element_key": row.element_key, - "formatted_value": row.formatted_value, - "separator": row.separator, - } - for row in literal.element_value_rows - ], - "product_used": literal.product_used.name if literal.product_used else None, - } - - if i < len(props.literal_apply_settings): - apply_settings = props.literal_apply_settings[i] - literal_data["apply_text_to_all"] = apply_settings.apply_text_to_all - literal_data["apply_path_to_all"] = apply_settings.apply_path_to_all - literal_data["apply_box_alignment_to_all"] = apply_settings.apply_box_alignment_to_all - else: - literal_data["apply_text_to_all"] = False - literal_data["apply_path_to_all"] = False - literal_data["apply_box_alignment_to_all"] = False - - captured_apply_settings["literals"].append(literal_data) - - obj["_bonsai_element_value_rows_backup"] = json.dumps(captured_apply_settings["literals"]) - - core.edit_text(tool.Drawing, obj=obj) - - self.apply_to_selected_objects_with_captured_data(context, obj, captured_apply_settings) - + apply_objs = [ + obj + for obj in tool.Blender.get_selected_objects() + if (element := tool.Ifc.get_entity(obj)) + and tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"]) + ] + core.edit_text(tool.Drawing, attribute_obj=tool.Blender.get_active_object(), apply_objs=apply_objs) tool.Blender.update_viewport() - return {"FINISHED"} - - def apply_to_selected_objects(self, context, active_obj, active_props): - """Apply changes to other selected text objects based on toggle settings""" - selected_objects = [obj for obj in context.selected_objects if obj != active_obj] - - for obj in selected_objects: - element = tool.Ifc.get_entity(obj) - if not element or not tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"]): - continue - - obj_props = tool.Drawing.get_text_props(obj) - needs_update = False - - if active_props.apply_font_size_to_all: - obj_props.font_size = active_props.font_size - needs_update = True - - if active_props.apply_newline_to_all: - obj_props.newline_at = active_props.newline_at - needs_update = True - - for i, active_literal in enumerate(active_props.literals): - if i >= len(obj_props.literals): - continue - - obj_props.ensure_literal_apply_settings(len(obj_props.literals)) - obj_literal = obj_props.literals[i] - - if i < len(active_props.literal_apply_settings): - active_settings = active_props.literal_apply_settings[i] - - if active_settings.apply_text_to_all: - if len(active_literal.attributes) > 0 and len(obj_literal.attributes) > 0: - obj_literal.attributes[0].string_value = active_literal.attributes[0].string_value - needs_update = True - - if active_settings.apply_path_to_all: - if len(active_literal.attributes) > 1 and len(obj_literal.attributes) > 1: - if ( - active_literal.attributes[1].data_type == "enum" - and obj_literal.attributes[1].data_type == "enum" - ): - obj_literal.attributes[1].enum_value = active_literal.attributes[1].enum_value - else: - obj_literal.attributes[1].string_value = active_literal.attributes[1].string_value - needs_update = True - - if active_settings.apply_box_alignment_to_all: - obj_literal.box_alignment = active_literal.box_alignment[:] - needs_update = True - - if needs_update: - core.edit_text(tool.Drawing, obj=obj) - - def apply_to_selected_objects_with_captured_data(self, context, active_obj, captured_data): - """Apply changes to other selected text objects using captured apply settings""" - selected_objects = [obj for obj in context.selected_objects if obj != active_obj] - - for obj in selected_objects: - element = tool.Ifc.get_entity(obj) - if not element: - continue - if not tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"]): - continue - - obj_props = tool.Drawing.get_text_props(obj) - - if len(obj_props.literals) == 0: - core.enable_editing_text(tool.Drawing, obj=obj) - obj_props.ensure_literal_apply_settings(len(obj_props.literals)) - - needs_update = False - - if captured_data["apply_font_size_to_all"]: - obj_props.font_size = captured_data["font_size"] - needs_update = True - - if captured_data["apply_newline_to_all"]: - obj_props.newline_at = captured_data["newline_at"] - needs_update = True - - for i, captured_literal in enumerate(captured_data["literals"]): - if i >= len(obj_props.literals): - continue - - obj_literal = obj_props.literals[i] - - if captured_literal["apply_text_to_all"]: - if len(captured_literal["attributes"]) > 0 and len(obj_literal.attributes) > 0: - new_value = captured_literal["attributes"][0][0] # [0] = string_value - obj_literal.attributes[0].string_value = new_value - needs_update = True - - if captured_literal["apply_path_to_all"]: - if len(captured_literal["attributes"]) > 1 and len(obj_literal.attributes) > 1: - new_value = captured_literal["attributes"][1][1] # [1] = enum_value or string_value - if obj_literal.attributes[1].data_type == "enum": - obj_literal.attributes[1].enum_value = new_value - else: - obj_literal.attributes[1].string_value = new_value - needs_update = True - - if captured_literal["apply_box_alignment_to_all"] and captured_literal["box_alignment"]: - obj_literal.box_alignment = captured_literal["box_alignment"] - needs_update = True - - if needs_update: - core.edit_text(tool.Drawing, obj=obj) - class EnableEditingText(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.enable_editing_text" diff --git a/src/bonsai/bonsai/core/drawing.py b/src/bonsai/bonsai/core/drawing.py index b56b51751f..eb2c47f1dc 100644 --- a/src/bonsai/bonsai/core/drawing.py +++ b/src/bonsai/bonsai/core/drawing.py @@ -38,11 +38,14 @@ def disable_editing_text(drawing: type[tool.Drawing], obj: bpy.types.Object) -> drawing.disable_editing_text(obj) -def edit_text(drawing: type[tool.Drawing], obj: bpy.types.Object) -> None: - drawing.synchronise_ifc_and_text_attributes(obj) - drawing.update_text_size_pset(obj) - drawing.update_text_annotation_properties(obj) - drawing.disable_editing_text(obj) +def edit_text(drawing: type[tool.Drawing], attribute_obj: bpy.types.Object, apply_objs: list[bpy.types.Object]) -> None: + literal_attributes = drawing.export_text_literal_attributes(attribute_obj) + for obj in apply_objs: + drawing.edit_text_literals(obj, literal_attributes) + # TODO: font size should be part of a separate set of formatting controls, not part of text editing + drawing.update_text_size_pset(obj) + drawing.update_text_annotation_properties(obj) + drawing.disable_editing_text(obj) def enable_editing_assigned_product(drawing: type[tool.Drawing], obj: bpy.types.Object) -> None: diff --git a/src/bonsai/bonsai/core/tool.py b/src/bonsai/bonsai/core/tool.py index 19395fa449..a34de4d4fe 100644 --- a/src/bonsai/bonsai/core/tool.py +++ b/src/bonsai/bonsai/core/tool.py @@ -402,7 +402,6 @@ class Drawing: def setup_shading_styles_path(cls, resource_path): pass def show_decorations(cls): pass def sync_object_placement(cls, obj): pass - def synchronise_ifc_and_text_attributes(cls, obj): pass def update_embedded_svg_location(cls, uri, old_location, new_location): pass def update_text_annotation_properties(cls, obj): pass def update_text_size_pset(cls, obj): pass diff --git a/src/bonsai/bonsai/tool/drawing.py b/src/bonsai/bonsai/tool/drawing.py index 51d06c1c18..a0a45b403a 100644 --- a/src/bonsai/bonsai/tool/drawing.py +++ b/src/bonsai/bonsai/tool/drawing.py @@ -835,43 +835,12 @@ class Drawing(bonsai.core.tool.Drawing): return props.is_editing_sheets @classmethod - def synchronise_ifc_and_text_attributes(cls, obj: bpy.types.Object) -> None: + def edit_text_literals(cls, obj: bpy.types.Object, literal_attributes: dict) -> None: assert (element := tool.Ifc.get_entity(obj)) assert (rep := cls.get_annotation_representation(element)) - - old_literals = cls.get_text_literal(obj, return_list=True) - assert isinstance(old_literals, list) - literals_attributes = cls.export_text_literal_attributes(obj) - props = cls.get_text_props(obj) - defined_ifc_ids = [l.ifc_definition_id for l in props.literals] - ifc_file = tool.Ifc.get() - - added_literals: list[ifcopenshell.entity_instance] = [] - new_literals: list[ifcopenshell.entity_instance] = [] - for ifc_definition_id, attributes in zip(defined_ifc_ids, literals_attributes): - # making sure all literals from text edit exist in ifc - if ifc_definition_id == 0: - literal = cls.add_literal(**attributes) - added_literals.append(literal) - else: - literal = ifc_file.by_id(ifc_definition_id) - ifcopenshell.api.drawing.edit_text_literal( - ifc_file, - text_literal=literal, - attributes=attributes, - ) - new_literals.append(literal) - - removed_literals = set(old_literals) - set(new_literals) - - # Add new literals and keep the order as defined in text props. - items = [i for i in rep.Items if i not in removed_literals] + added_literals - items.sort(key=lambda x: new_literals.index(x) if x in new_literals else -1) - rep.Items = items - - # Remove from ifc the literals that were removed during the edit. - for literal in removed_literals: - ifcopenshell.util.element.remove_deep2(ifc_file, literal) + for literal in cls.get_text_literal(obj, return_list=True): + ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), literal) + rep.Items = [cls.add_literal(**a) for a in literal_attributes] @classmethod def add_literal(cls, **attributes: str) -> ifcopenshell.entity_instance: