Skip to content

Commit 1445f89

Browse files
committed
See #7629. Partially reimplement bulk text editing.
This isn't complete yet, but it hopefully demonstrates a preferred implementation: * Logic in core, not operator * Loop done in core, without needing to call other core functions, so the overhead of enabling and disabling editing per object is removed. No more Blender logic, just straight editing in IFC. * Reuse existing function to grab text attributes instead of reimplementing it twice. * Remove dead code, there seems to be a function apply_to_selected_objects which was completely unused and duplicated code twice.
1 parent fe395f9 commit 1445f89

4 files changed

Lines changed: 19 additions & 199 deletions

File tree

src/bonsai/bonsai/bim/module/drawing/operator.py

Lines changed: 7 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -3185,166 +3185,15 @@ class EditText(bpy.types.Operator, tool.Ifc.Operator):
31853185
bl_options = {"REGISTER", "UNDO"}
31863186

31873187
def _execute(self, context):
3188-
obj = context.active_object
3189-
props = tool.Drawing.get_text_props(obj)
3190-
3191-
captured_apply_settings = {
3192-
"apply_font_size_to_all": props.apply_font_size_to_all,
3193-
"apply_newline_to_all": props.apply_newline_to_all,
3194-
"font_size": props.font_size,
3195-
"newline_at": props.newline_at,
3196-
"literals": [],
3197-
}
3198-
3199-
for i, literal in enumerate(props.literals):
3200-
literal_data = {
3201-
"attributes": [
3202-
(attr.string_value, attr.enum_value if attr.data_type == "enum" else attr.string_value)
3203-
for attr in literal.attributes
3204-
],
3205-
"box_alignment": literal.box_alignment[:] if hasattr(literal, "box_alignment") else None,
3206-
"element_value_rows": [
3207-
{
3208-
"category": row.category,
3209-
"element_key": row.element_key,
3210-
"formatted_value": row.formatted_value,
3211-
"separator": row.separator,
3212-
}
3213-
for row in literal.element_value_rows
3214-
],
3215-
"product_used": literal.product_used.name if literal.product_used else None,
3216-
}
3217-
3218-
if i < len(props.literal_apply_settings):
3219-
apply_settings = props.literal_apply_settings[i]
3220-
literal_data["apply_text_to_all"] = apply_settings.apply_text_to_all
3221-
literal_data["apply_path_to_all"] = apply_settings.apply_path_to_all
3222-
literal_data["apply_box_alignment_to_all"] = apply_settings.apply_box_alignment_to_all
3223-
else:
3224-
literal_data["apply_text_to_all"] = False
3225-
literal_data["apply_path_to_all"] = False
3226-
literal_data["apply_box_alignment_to_all"] = False
3227-
3228-
captured_apply_settings["literals"].append(literal_data)
3229-
3230-
obj["_bonsai_element_value_rows_backup"] = json.dumps(captured_apply_settings["literals"])
3231-
3232-
core.edit_text(tool.Drawing, obj=obj)
3233-
3234-
self.apply_to_selected_objects_with_captured_data(context, obj, captured_apply_settings)
3235-
3188+
apply_objs = [
3189+
obj
3190+
for obj in tool.Blender.get_selected_objects()
3191+
if (element := tool.Ifc.get_entity(obj))
3192+
and tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"])
3193+
]
3194+
core.edit_text(tool.Drawing, attribute_obj=tool.Blender.get_active_object(), apply_objs=apply_objs)
32363195
tool.Blender.update_viewport()
32373196

3238-
return {"FINISHED"}
3239-
3240-
def apply_to_selected_objects(self, context, active_obj, active_props):
3241-
"""Apply changes to other selected text objects based on toggle settings"""
3242-
selected_objects = [obj for obj in context.selected_objects if obj != active_obj]
3243-
3244-
for obj in selected_objects:
3245-
element = tool.Ifc.get_entity(obj)
3246-
if not element or not tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"]):
3247-
continue
3248-
3249-
obj_props = tool.Drawing.get_text_props(obj)
3250-
needs_update = False
3251-
3252-
if active_props.apply_font_size_to_all:
3253-
obj_props.font_size = active_props.font_size
3254-
needs_update = True
3255-
3256-
if active_props.apply_newline_to_all:
3257-
obj_props.newline_at = active_props.newline_at
3258-
needs_update = True
3259-
3260-
for i, active_literal in enumerate(active_props.literals):
3261-
if i >= len(obj_props.literals):
3262-
continue
3263-
3264-
obj_props.ensure_literal_apply_settings(len(obj_props.literals))
3265-
obj_literal = obj_props.literals[i]
3266-
3267-
if i < len(active_props.literal_apply_settings):
3268-
active_settings = active_props.literal_apply_settings[i]
3269-
3270-
if active_settings.apply_text_to_all:
3271-
if len(active_literal.attributes) > 0 and len(obj_literal.attributes) > 0:
3272-
obj_literal.attributes[0].string_value = active_literal.attributes[0].string_value
3273-
needs_update = True
3274-
3275-
if active_settings.apply_path_to_all:
3276-
if len(active_literal.attributes) > 1 and len(obj_literal.attributes) > 1:
3277-
if (
3278-
active_literal.attributes[1].data_type == "enum"
3279-
and obj_literal.attributes[1].data_type == "enum"
3280-
):
3281-
obj_literal.attributes[1].enum_value = active_literal.attributes[1].enum_value
3282-
else:
3283-
obj_literal.attributes[1].string_value = active_literal.attributes[1].string_value
3284-
needs_update = True
3285-
3286-
if active_settings.apply_box_alignment_to_all:
3287-
obj_literal.box_alignment = active_literal.box_alignment[:]
3288-
needs_update = True
3289-
3290-
if needs_update:
3291-
core.edit_text(tool.Drawing, obj=obj)
3292-
3293-
def apply_to_selected_objects_with_captured_data(self, context, active_obj, captured_data):
3294-
"""Apply changes to other selected text objects using captured apply settings"""
3295-
selected_objects = [obj for obj in context.selected_objects if obj != active_obj]
3296-
3297-
for obj in selected_objects:
3298-
element = tool.Ifc.get_entity(obj)
3299-
if not element:
3300-
continue
3301-
if not tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"]):
3302-
continue
3303-
3304-
obj_props = tool.Drawing.get_text_props(obj)
3305-
3306-
if len(obj_props.literals) == 0:
3307-
core.enable_editing_text(tool.Drawing, obj=obj)
3308-
obj_props.ensure_literal_apply_settings(len(obj_props.literals))
3309-
3310-
needs_update = False
3311-
3312-
if captured_data["apply_font_size_to_all"]:
3313-
obj_props.font_size = captured_data["font_size"]
3314-
needs_update = True
3315-
3316-
if captured_data["apply_newline_to_all"]:
3317-
obj_props.newline_at = captured_data["newline_at"]
3318-
needs_update = True
3319-
3320-
for i, captured_literal in enumerate(captured_data["literals"]):
3321-
if i >= len(obj_props.literals):
3322-
continue
3323-
3324-
obj_literal = obj_props.literals[i]
3325-
3326-
if captured_literal["apply_text_to_all"]:
3327-
if len(captured_literal["attributes"]) > 0 and len(obj_literal.attributes) > 0:
3328-
new_value = captured_literal["attributes"][0][0] # [0] = string_value
3329-
obj_literal.attributes[0].string_value = new_value
3330-
needs_update = True
3331-
3332-
if captured_literal["apply_path_to_all"]:
3333-
if len(captured_literal["attributes"]) > 1 and len(obj_literal.attributes) > 1:
3334-
new_value = captured_literal["attributes"][1][1] # [1] = enum_value or string_value
3335-
if obj_literal.attributes[1].data_type == "enum":
3336-
obj_literal.attributes[1].enum_value = new_value
3337-
else:
3338-
obj_literal.attributes[1].string_value = new_value
3339-
needs_update = True
3340-
3341-
if captured_literal["apply_box_alignment_to_all"] and captured_literal["box_alignment"]:
3342-
obj_literal.box_alignment = captured_literal["box_alignment"]
3343-
needs_update = True
3344-
3345-
if needs_update:
3346-
core.edit_text(tool.Drawing, obj=obj)
3347-
33483197

33493198
class EnableEditingText(bpy.types.Operator, tool.Ifc.Operator):
33503199
bl_idname = "bim.enable_editing_text"

src/bonsai/bonsai/core/drawing.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@ def disable_editing_text(drawing: type[tool.Drawing], obj: bpy.types.Object) ->
3838
drawing.disable_editing_text(obj)
3939

4040

41-
def edit_text(drawing: type[tool.Drawing], obj: bpy.types.Object) -> None:
42-
drawing.synchronise_ifc_and_text_attributes(obj)
43-
drawing.update_text_size_pset(obj)
44-
drawing.update_text_annotation_properties(obj)
45-
drawing.disable_editing_text(obj)
41+
def edit_text(drawing: type[tool.Drawing], attribute_obj: bpy.types.Object, apply_objs: list[bpy.types.Object]) -> None:
42+
literal_attributes = drawing.export_text_literal_attributes(attribute_obj)
43+
for obj in apply_objs:
44+
drawing.edit_text_literals(obj, literal_attributes)
45+
# TODO: font size should be part of a separate set of formatting controls, not part of text editing
46+
drawing.update_text_size_pset(obj)
47+
drawing.update_text_annotation_properties(obj)
48+
drawing.disable_editing_text(obj)
4649

4750

4851
def enable_editing_assigned_product(drawing: type[tool.Drawing], obj: bpy.types.Object) -> None:

src/bonsai/bonsai/core/tool.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,6 @@ def setup_annotation_object(cls, obj, object_type): pass
402402
def setup_shading_styles_path(cls, resource_path): pass
403403
def show_decorations(cls): pass
404404
def sync_object_placement(cls, obj): pass
405-
def synchronise_ifc_and_text_attributes(cls, obj): pass
406405
def update_embedded_svg_location(cls, uri, old_location, new_location): pass
407406
def update_text_annotation_properties(cls, obj): pass
408407
def update_text_size_pset(cls, obj): pass

src/bonsai/bonsai/tool/drawing.py

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -835,43 +835,12 @@ def is_editing_sheets(cls) -> bool:
835835
return props.is_editing_sheets
836836

837837
@classmethod
838-
def synchronise_ifc_and_text_attributes(cls, obj: bpy.types.Object) -> None:
838+
def edit_text_literals(cls, obj: bpy.types.Object, literal_attributes: dict) -> None:
839839
assert (element := tool.Ifc.get_entity(obj))
840840
assert (rep := cls.get_annotation_representation(element))
841-
842-
old_literals = cls.get_text_literal(obj, return_list=True)
843-
assert isinstance(old_literals, list)
844-
literals_attributes = cls.export_text_literal_attributes(obj)
845-
props = cls.get_text_props(obj)
846-
defined_ifc_ids = [l.ifc_definition_id for l in props.literals]
847-
ifc_file = tool.Ifc.get()
848-
849-
added_literals: list[ifcopenshell.entity_instance] = []
850-
new_literals: list[ifcopenshell.entity_instance] = []
851-
for ifc_definition_id, attributes in zip(defined_ifc_ids, literals_attributes):
852-
# making sure all literals from text edit exist in ifc
853-
if ifc_definition_id == 0:
854-
literal = cls.add_literal(**attributes)
855-
added_literals.append(literal)
856-
else:
857-
literal = ifc_file.by_id(ifc_definition_id)
858-
ifcopenshell.api.drawing.edit_text_literal(
859-
ifc_file,
860-
text_literal=literal,
861-
attributes=attributes,
862-
)
863-
new_literals.append(literal)
864-
865-
removed_literals = set(old_literals) - set(new_literals)
866-
867-
# Add new literals and keep the order as defined in text props.
868-
items = [i for i in rep.Items if i not in removed_literals] + added_literals
869-
items.sort(key=lambda x: new_literals.index(x) if x in new_literals else -1)
870-
rep.Items = items
871-
872-
# Remove from ifc the literals that were removed during the edit.
873-
for literal in removed_literals:
874-
ifcopenshell.util.element.remove_deep2(ifc_file, literal)
841+
for literal in cls.get_text_literal(obj, return_list=True):
842+
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), literal)
843+
rep.Items = [cls.add_literal(**a) for a in literal_attributes]
875844

876845
@classmethod
877846
def add_literal(cls, **attributes: str) -> ifcopenshell.entity_instance:

0 commit comments

Comments
 (0)