Fix #5220. See #7629. See #7505. Reimplement text bulk copying using established copy attribute paradigm.

Previously, copy attribution was coupled with text editing. This meant
that you couldn't just do something like change the font or alignment
without also affecting literals. Now like most apps you can just select
bunch of text and change font size etc, using the same UI look and feel
that copying attribute has when editing attributes.

This refactor also removes the need for explicit props tracking each
possible attribute to copy, and the settings collection group. Bulk
applying is now done in core with no calls to UI.
This commit is contained in:
Dion Moult
2026-02-06 16:12:14 +11:00
parent 2692341d3a
commit 5e9f97a0c7
9 changed files with 192 additions and 193 deletions
@@ -45,6 +45,7 @@ classes = (
operator.CleanWireframes,
operator.ContractSheet,
operator.ConvertSVGToDXF,
operator.CopyTextToSelection,
operator.CreateDrawing,
operator.CreateSheets,
operator.DisableAddAnnotationType,
@@ -115,7 +116,6 @@ classes = (
prop.BIMCameraProperties,
prop.ElementValueRow,
prop.LiteralProps,
prop.LiteralApplySettings,
prop.BIMTextProperties,
prop.BIMAssignedProductProperties,
prop.BIMAnnotationProperties,
@@ -355,8 +355,6 @@ class DecoratorData:
font_size = FONT_SIZES[font_size_type]
symbol = tool.Drawing.get_annotation_symbol(element)
newline_at = pset_data.get("Newline_At", 0)
reverse_list = pset_data.get("Reverse_List", False)
list_separator = pset_data.get("List_Separator") or ", "
# other attributes
literals = tool.Drawing.get_text_literal(obj, return_list=True)
@@ -384,8 +382,6 @@ class DecoratorData:
"FontSize": font_size,
"Symbol": symbol,
"Newline_At": newline_at,
"Reverse_List": reverse_list,
"List_Separator": list_separator,
}
@classmethod
@@ -3181,9 +3181,20 @@ class EditText(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_text"
bl_label = "Edit Text"
bl_description = "Save changes to the text annotation and\ndisable the text editing options"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
core.edit_text(tool.Drawing, obj=tool.Blender.get_active_object())
tool.Blender.update_viewport()
class CopyTextToSelection(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.copy_text_to_selection"
bl_label = "Copy Text To Selection"
bl_description = "Copy text formatting or literals to selected objects"
bl_options = {"REGISTER", "UNDO"}
attribute: bpy.props.StringProperty()
def _execute(self, context):
apply_objs = [
obj
@@ -3191,7 +3202,12 @@ class EditText(bpy.types.Operator, tool.Ifc.Operator):
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)
core.copy_text_to_selection(
tool.Drawing,
attribute=self.attribute,
attribute_obj=tool.Blender.get_active_object(),
apply_objs=apply_objs,
)
tool.Blender.update_viewport()
@@ -3207,8 +3223,6 @@ class EnableEditingText(bpy.types.Operator, tool.Ifc.Operator):
props = tool.Drawing.get_text_props(obj)
core.enable_editing_text(tool.Drawing, obj=obj)
props.ensure_literal_apply_settings(len(props.literals))
text_element = tool.Ifc.get_entity(obj)
assigned_product_entity = tool.Drawing.get_assigned_product(text_element) if text_element else None
assigned_product_obj = tool.Ifc.get_object(assigned_product_entity) if assigned_product_entity else None
@@ -3297,9 +3311,6 @@ class AddTextLiteral(bpy.types.Operator):
box_alignment_mask = [False] * 9
box_alignment_mask[6] = True # bottom_left box_alignment
literal_props.box_alignment = box_alignment_mask
props.ensure_literal_apply_settings(len(props.literals))
return {"FINISHED"}
@@ -3318,9 +3329,6 @@ class RemoveTextLiteral(bpy.types.Operator):
props = tool.Drawing.get_text_props(obj)
props.literals.remove(self.literal_prop_id)
tool.Blender.update_viewport()
props.ensure_literal_apply_settings(len(props.literals))
return {"FINISHED"}
+30 -52
View File
@@ -872,22 +872,20 @@ class LiteralProps(PropertyGroup):
category_for_adding: str
class LiteralApplySettings(PropertyGroup):
literal_index: IntProperty(name="Literal Index")
apply_text_to_all: BoolProperty(name="Apply Text to All", default=False)
apply_path_to_all: BoolProperty(name="Apply Path to All", default=False)
apply_box_alignment_to_all: BoolProperty(name="Apply Box Alignment to All", default=False)
if TYPE_CHECKING:
literal_index: int
apply_text_to_all: bool
apply_path_to_all: bool
apply_box_alignment_to_all: bool
class BIMTextProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing", default=False)
literals: CollectionProperty(name="Literals", type=LiteralProps)
newline_at: IntProperty(name="Newline At")
symbol: EnumProperty( # pyright: ignore[reportRedeclaration]
name="Symbol",
description="Symbol from symbols.svg to use for this text.",
items=[(s, s, "") for s in ["NO SYMBOL", "CUSTOM SYMBOL"] + tool.Drawing.DEFAULT_SYMBOLS],
default="NO SYMBOL",
)
custom_symbol: StringProperty( # pyright: ignore[reportRedeclaration]
name="Custom Symbol",
description="Non-default symbol to use for this text.",
)
font_size: EnumProperty(
items=[
("1.8", "1.8 - Small", ""),
@@ -899,52 +897,34 @@ class BIMTextProperties(PropertyGroup):
default="2.5",
name="Font Size",
)
newline_at: IntProperty(name="Newline At")
reverse_list: BoolProperty(name="Reverse List", description="Reverses the order of any list.", default=False)
list_separator: StringProperty( # pyright: ignore[reportRedeclaration]
name="List Separator",
description="Text used to separate lists. Uses a comma (, ) if empty.",
align_horizontal: EnumProperty(
items=[
("left", "Left", "", "ALIGN_LEFT", 0),
("middle", "Middle", "", "ALIGN_CENTER", 1),
("right", "Right", "", "ALIGN_RIGHT", 2),
],
default="left",
name="Horizontal Alignment",
)
symbol: EnumProperty( # pyright: ignore[reportRedeclaration]
name="Symbol",
description="Symbol from symbols.svg to use for this text.",
items=[(s, s, "") for s in ["NO SYMBOL", "CUSTOM SYMBOL"] + tool.Drawing.DEFAULT_SYMBOLS],
default="NO SYMBOL",
align_vertical: EnumProperty(
items=[
("top", "Top", "", "ALIGN_TOP", 0),
("middle", "Middle", "", "ALIGN_MIDDLE", 1),
("bottom", "Bottom", "", "ALIGN_BOTTOM", 2),
],
default="middle",
name="Vertical Alignment",
)
custom_symbol: StringProperty( # pyright: ignore[reportRedeclaration]
name="Custom Symbol",
description="Non-default symbol to use for this text.",
)
apply_font_size_to_all: BoolProperty(
name="Apply Font Size to All", description="Apply font size changes to all selected text objects", default=False
)
apply_newline_to_all: BoolProperty(
name="Apply Newline to All", description="Apply newline changes to all selected text objects", default=False
)
literal_apply_settings: CollectionProperty(name="Literal Apply Settings", type=LiteralApplySettings)
def ensure_literal_apply_settings(self, literal_count: int):
"""Ensure we have apply settings for all literals"""
while len(self.literal_apply_settings) > literal_count:
self.literal_apply_settings.remove(len(self.literal_apply_settings) - 1)
while len(self.literal_apply_settings) < literal_count:
setting = self.literal_apply_settings.add()
setting.literal_index = len(self.literal_apply_settings) - 1
if TYPE_CHECKING:
is_editing: bool
literals: bpy.types.bpy_prop_collection_idprop[LiteralProps]
font_size: str
newline_at: int
reverse_list: bool
list_separator: str
symbol: Union[str, Literal["NO SYMBOL", "CUSTOM SYMBOL"]]
custom_symbol: str
apply_font_size_to_all: bool
apply_newline_to_all: bool
font_size: str
align_horizontal: str
align_vertical: str
def get_symbol(self) -> Union[str, None]:
if self.symbol == "NO SYMBOL":
@@ -979,8 +959,6 @@ class BIMTextProperties(PropertyGroup):
"FontSize": float(self.font_size),
"Newline_At": int(self.newline_at),
"Symbol": self.get_symbol(),
"Reverse_List": self.reverse_list,
"List_Separator": self.list_separator or ", ",
}
return text_data
@@ -989,11 +989,6 @@ class SvgWriter:
symbol = tool.Drawing.get_annotation_symbol(element)
newline_at = tool.Drawing.get_newline_at(element)
# Get reverse_list and list_separator from EPset_Annotation
pset_data = ifcopenshell.util.element.get_pset(element, "EPset_Annotation") or {}
reverse_list = pset_data.get("Reverse_List", False)
list_separator = pset_data.get("List_Separator") or ", "
template_text_fields = []
if symbol:
symbol_transform = self.get_symbol_transform(text_position_svg_str, angle, text_obj)
@@ -1010,7 +1005,7 @@ class SvgWriter:
# NOTE: zip makes sure that we iterate over the shortest list
for field, text_literal in zip(template_text_fields, text_literals):
field.text = tool.Drawing.replace_text_literal_variables(
text_literal.Literal, product or element, reverse_list, list_separator
text_literal.Literal, product or element
)
field.attrib["class"] = classes_str
@@ -1030,9 +1025,7 @@ class SvgWriter:
line_number = 0
for text_literal in text_literals:
text = tool.Drawing.replace_text_literal_variables(
text_literal.Literal, product or element, reverse_list, list_separator
)
text = tool.Drawing.replace_text_literal_variables(text_literal.Literal, product or element)
text_segments = parse_markdown_it(text)
+60 -77
View File
@@ -644,58 +644,56 @@ class BIM_PT_text(Panel):
row.operator("bim.add_text_literal", icon="ADD", text="Add Literal")
else:
row.operator("bim.edit_text", icon="CHECKMARK")
row.operator("bim.add_text_literal", icon="ADD", text="")
row.operator("bim.disable_editing_text", icon="CANCEL", text="")
row = self.layout.row(align=True)
row.prop(props, "font_size")
row.prop(props, "apply_font_size_to_all", text="", icon="COPYDOWN")
row.operator("bim.copy_text_to_selection", text="", icon="COPYDOWN").attribute = "FONT_SIZE"
row = self.layout.row()
row.label(text="Alignment")
row.prop(props, "align_horizontal", text="", expand=True)
row.prop(props, "align_vertical", text="", expand=True)
row.operator("bim.copy_text_to_selection", text="", icon="COPYDOWN").attribute = "ALIGNMENT"
row = self.layout.row(align=True)
row.prop(props, "newline_at")
row = self.layout.row(align=True)
row.prop(props, "reverse_list")
row = self.layout.row(align=True)
row.prop(props, "list_separator")
row.operator("bim.copy_text_to_selection", text="", icon="COPYDOWN").attribute = "WRAP_LENGTH"
row = self.layout.row(align=True)
row.prop(props, "symbol")
if props.symbol == "CUSTOM SYMBOL":
row = self.layout.row(align=True)
row.prop(props, "custom_symbol", text="")
row.prop(props, "apply_newline_to_all", text="", icon="COPYDOWN")
select_op = row.operator("bim.select_similar_text_literal_value", text="", icon="RESTRICT_SELECT_OFF")
select_op.literal_value = str(props.newline_at)
select_op.attribute_type = "newline"
row.operator("bim.copy_text_to_selection", text="", icon="COPYDOWN").attribute = "SYMBOL"
row = self.layout.row(align=True)
row.label(text="Literals:")
row.operator("bim.copy_text_to_selection", text="", icon="COPYDOWN").attribute = "LITERALS"
row.operator("bim.add_text_literal", icon="ADD", text="")
for i, literal_props in enumerate(props.literals):
box = self.layout.box()
row = box.row(align=True)
row.label(text=f"Literal[{i}]:")
if i > 0:
row.operator("bim.order_text_literal_up", icon="TRIA_UP", text="").literal_prop_id = i
if i < len(props.literals) - 1:
row.operator("bim.order_text_literal_down", icon="TRIA_DOWN", text="").literal_prop_id = i
row.operator("bim.remove_text_literal", icon="X", text="").literal_prop_id = i
if len(literal_props.attributes) > 0 and i < len(props.literal_apply_settings):
if len(literal_props.attributes):
row = box.row(align=True)
bonsai.bim.helper.draw_attribute(literal_props.attributes[0], row, enable_search=True)
if i > 0:
row.operator("bim.order_text_literal_up", icon="TRIA_UP", text="").literal_prop_id = i
if i < len(props.literals) - 1:
row.operator("bim.order_text_literal_down", icon="TRIA_DOWN", text="").literal_prop_id = i
row.operator("bim.remove_text_literal", icon="X", text="").literal_prop_id = i
expand_icon = "DOWNARROW_HLT" if getattr(literal_props, "show_element_values", False) else "RIGHTARROW"
op = row.operator("bim.toggle_element_values_panel", icon=expand_icon, text="")
op.literal_prop_id = i
row.prop(props.literal_apply_settings[i], "apply_text_to_all", text="", icon="COPYDOWN")
element = tool.Ifc.get_entity(obj)
assigned_element = tool.Drawing.get_assigned_product(element) or element
resolved_value = tool.Drawing.replace_text_literal_variables(
literal_props.attributes[0].string_value,
assigned_element,
props.reverse_list,
props.list_separator,
)
row = box.row(align=True)
row.label(text="CurrentValue:")
@@ -779,8 +777,6 @@ class BIM_PT_text(Panel):
else:
row.prop(attr, "string_value", text="Path")
select_value = attr.string_value
if i < len(props.literal_apply_settings):
row.prop(props.literal_apply_settings[i], "apply_path_to_all", text="", icon="COPYDOWN")
other_attributes = [a for a in literal_props.attributes[2:] if a.name != "BoxAlignment"]
if other_attributes:
@@ -800,10 +796,6 @@ class BIM_PT_text(Panel):
col = row.column(align=True)
alignment_label_row = col.row(align=True)
alignment_label_row.label(text=" Text box alignment:")
if i < len(props.literal_apply_settings):
alignment_label_row.prop(
props.literal_apply_settings[i], "apply_box_alignment_to_all", text="", icon="COPYDOWN"
)
box_alignment_value = (
literal_props.attributes[
@@ -824,61 +816,52 @@ class BIM_PT_text(Panel):
props = tool.Drawing.get_text_props(obj)
if props.is_editing:
self.draw_text_editing_ui(context)
else:
text_data = DecoratorData.get_text_data(obj)
return self.draw_text_editing_ui(context)
text_data = DecoratorData.get_text_data(obj)
row = self.layout.row()
row.operator("bim.enable_editing_text", icon="GREASEPENCIL")
row = self.layout.row()
row.operator("bim.enable_editing_text", icon="GREASEPENCIL")
row = self.layout.row(align=True)
row.label(text="FontSize")
click_op = row.operator(
"bim.select_similar_text_literal_value", text=str(text_data["FontSize"]), emboss=False
)
click_op.literal_value = str(text_data["FontSize"])
click_op.attribute_type = "font_size"
click_op.display_text = str(text_data["FontSize"])
row = self.layout.row(align=True)
row.label(text="FontSize")
click_op = row.operator("bim.select_similar_text_literal_value", text=str(text_data["FontSize"]), emboss=False)
click_op.literal_value = str(text_data["FontSize"])
click_op.attribute_type = "font_size"
click_op.display_text = str(text_data["FontSize"])
row = self.layout.row(align=True)
row.label(text="Newline_At")
click_op = row.operator(
"bim.select_similar_text_literal_value", text=str(text_data["Newline_At"]), emboss=False
)
click_op.literal_value = str(text_data["Newline_At"])
click_op.attribute_type = "newline"
click_op.display_text = str(text_data["Newline_At"])
row = self.layout.row(align=True)
row.label(text="Reverse_List")
row.label(text=str(text_data["Reverse_List"]))
row = self.layout.row(align=True)
row.label(text="List_Separator")
row.label(text=str(text_data["List_Separator"]))
row = self.layout.row(align=True)
row.label(text="Newline_At")
click_op = row.operator(
"bim.select_similar_text_literal_value", text=str(text_data["Newline_At"]), emboss=False
)
click_op.literal_value = str(text_data["Newline_At"])
click_op.attribute_type = "newline"
click_op.display_text = str(text_data["Newline_At"])
for i, literal_data in enumerate(text_data["Literals"]):
box = self.layout.box()
box.label(text=f"Literal[{i}]:")
for i, literal_data in enumerate(text_data["Literals"]):
box = self.layout.box()
box.label(text=f"Literal[{i}]:")
# Combine both approaches: clickable attributes from PR #7292 and display from PR #7106
for attribute in literal_data:
row = box.row(align=True)
row.label(text=attribute)
click_op = row.operator(
"bim.select_similar_text_literal_value",
text=str(literal_data[attribute]),
emboss=False,
)
click_op.literal_value = str(literal_data[attribute])
click_op.literal_index = i
if attribute == "Literal":
click_op.attribute_type = "literal"
elif attribute == "Path":
click_op.attribute_type = "path"
elif attribute == "BoxAlignment":
click_op.attribute_type = "box_alignment"
else:
click_op.attribute_type = "text"
click_op.display_text = str(literal_data[attribute])
# Combine both approaches: clickable attributes from PR #7292 and display from PR #7106
for attribute in literal_data:
row = box.row(align=True)
row.label(text=attribute)
click_op = row.operator(
"bim.select_similar_text_literal_value",
text=str(literal_data[attribute]),
emboss=False,
)
click_op.literal_value = str(literal_data[attribute])
click_op.literal_index = i
if attribute == "Literal":
click_op.attribute_type = "literal"
elif attribute == "Path":
click_op.attribute_type = "path"
elif attribute == "BoxAlignment":
click_op.attribute_type = "box_alignment"
else:
click_op.attribute_type = "text"
click_op.display_text = str(literal_data[attribute])
class BIM_UL_drawinglist(bpy.types.UIList):
+35 -6
View File
@@ -38,13 +38,42 @@ def disable_editing_text(drawing: type[tool.Drawing], obj: bpy.types.Object) ->
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)
def edit_text(drawing: type[tool.Drawing], obj: bpy.types.Object) -> None:
literal_attributes = drawing.export_text_literal_attributes(obj)
drawing.edit_text_font_size(obj, drawing.export_font_size(obj))
drawing.edit_text_wrap_length(obj, drawing.export_wrap_length(obj))
drawing.edit_text_symbol(obj, drawing.export_symbol(obj))
drawing.edit_text_literals(obj, literal_attributes)
drawing.disable_editing_text(obj)
def copy_text_to_selection(
drawing: type[tool.Drawing],
attribute: Literal["FONT_SIZE", "ALIGNMENT", "WRAP_LENGTH", "SYMBOL", "LITERALS"],
attribute_obj: bpy.types.Object,
apply_objs: list[bpy.types.Object],
) -> None:
if attribute == "FONT_SIZE":
data = drawing.export_font_size(attribute_obj)
elif attribute == "ALIGNMENT":
data = drawing.export_alignment(attribute_obj)
elif attribute == "WRAP_LENGTH":
data = drawing.export_wrap_length(attribute_obj)
elif attribute == "SYMBOL":
data = drawing.export_symbol(attribute_obj)
elif attribute == "LITERALS":
data = 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)
if attribute == "FONT_SIZE":
drawing.edit_text_font_size(obj, data)
elif attribute == "ALIGNMENT":
drawing.edit_text_alignment(obj, data)
elif attribute == "WRAP_LENGTH":
drawing.edit_text_wrap_length(obj, data)
elif attribute == "SYMBOL":
drawing.edit_text_symbol(obj, data)
elif attribute == "LITERALS":
drawing.edit_text_literals(obj, data)
drawing.disable_editing_text(obj)
+5 -2
View File
@@ -334,6 +334,11 @@ class Drawing:
def disable_editing_sheets(cls): pass
def disable_editing_text(cls, obj): pass
def does_file_exist(cls, uri): pass
def edit_text_alignment(cls, obj, alignment): pass
def edit_text_font_size(cls, obj, size): pass
def edit_text_literals(cls, obj, literals): pass
def edit_text_symbol(cls, obj, symbol): pass
def edit_text_wrap_length(cls, obj, wrap_length): pass
def enable_editing(cls, obj): pass
def enable_editing_assigned_product(cls, obj): pass
def enable_editing_drawings(cls): pass
@@ -403,8 +408,6 @@ class Drawing:
def show_decorations(cls): pass
def sync_object_placement(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
@interface
+41 -32
View File
@@ -608,6 +608,25 @@ class Drawing(bonsai.core.tool.Drawing):
literals.append(literal_data)
return literals
@classmethod
def export_font_size(cls, obj: bpy.types.Object) -> str:
return float(cls.get_text_props(obj).font_size)
@classmethod
def export_alignment(cls, obj: bpy.types.Object) -> str:
props = cls.get_text_props(obj)
if (alignment := props.align_vertical + "-" + props.align_horizontal) == "middle-middle":
return "center"
return alignment
@classmethod
def export_wrap_length(cls, obj: bpy.types.Object) -> str:
return cls.get_text_props(obj).newline_at
@classmethod
def export_symbol(cls, obj: bpy.types.Object) -> str:
return cls.get_text_props(obj).get_symbol()
@classmethod
def create_annotation_context(
cls, target_view: str, object_type: Optional[str] = None
@@ -1181,8 +1200,6 @@ class Drawing(bonsai.core.tool.Drawing):
props.font_size = str(text_data["FontSize"])
props.newline_at = text_data["Newline_At"]
props.set_symbol(text_data["Symbol"])
props.reverse_list = text_data["Reverse_List"]
props.list_separator = text_data["List_Separator"]
@classmethod
def import_assigned_product(cls, obj: bpy.types.Object) -> None:
@@ -1279,7 +1296,7 @@ class Drawing(bonsai.core.tool.Drawing):
props.should_draw_decorations = True
@classmethod
def update_text_size_pset(cls, obj: bpy.types.Object) -> None:
def edit_text_font_size(cls, obj: bpy.types.Object, font_size: float) -> None:
"""updates pset `EPset_Annotation.Classes` value
based on current font size from `obj.BIMTextProperties.font_size`
"""
@@ -1289,8 +1306,9 @@ class Drawing(bonsai.core.tool.Drawing):
element = tool.Ifc.get_entity(obj)
assert element
# updating text font size in EPset_Annotation.Classes
font_size = float(props.font_size)
print("we got", font_size, repr(font_size))
font_size_str = next((key for key in FONT_SIZES if FONT_SIZES[key] == font_size), None)
print("so", font_size_str)
classes = ifcopenshell.util.element.get_pset(element, "EPset_Annotation", "Classes")
assert isinstance(classes, Union[str, None])
classes_split = classes.split() if classes else []
@@ -1310,34 +1328,32 @@ class Drawing(bonsai.core.tool.Drawing):
pset = tool.Pset.get_element_pset(element, "EPset_Annotation")
if not pset:
pset = ifcopenshell.api.pset.add_pset(ifc_file, product=element, name="EPset_Annotation")
ifcopenshell.api.pset.edit_pset(
ifc_file,
pset=pset,
properties={"Classes": classes},
)
ifcopenshell.api.pset.edit_pset(ifc_file, pset=pset, properties={"Classes": classes})
@classmethod
def update_text_annotation_properties(cls, obj: bpy.types.Object) -> None:
"""Update all EPset_Annotation properties from the text props"""
props = cls.get_text_props(obj)
def edit_text_wrap_length(cls, obj: bpy.types.Object, wrap_length: int) -> None:
element = tool.Ifc.get_entity(obj)
assert element
ifc_file = tool.Ifc.get()
pset = tool.Pset.get_element_pset(element, "EPset_Annotation")
if not pset:
pset = ifcopenshell.api.pset.add_pset(ifc_file, product=element, name="EPset_Annotation")
ifcopenshell.api.pset.edit_pset(ifc_file, pset=pset, properties={"Newline_At": wrap_length})
ifcopenshell.api.pset.edit_pset(
ifc_file,
pset=pset,
properties={
"Newline_At": int(props.newline_at),
"Symbol": props.get_symbol(),
"Reverse_List": props.reverse_list,
"List_Separator": props.list_separator or "",
},
)
@classmethod
def edit_text_symbol(cls, obj: bpy.types.Object, symbol: str) -> None:
element = tool.Ifc.get_entity(obj)
ifc_file = tool.Ifc.get()
pset = tool.Pset.get_element_pset(element, "EPset_Annotation")
if not pset:
pset = ifcopenshell.api.pset.add_pset(ifc_file, product=element, name="EPset_Annotation")
ifcopenshell.api.pset.edit_pset(ifc_file, pset=pset, properties={"Symbol": symbol})
@classmethod
def edit_text_alignment(cls, obj: bpy.types.Object, alignment: str) -> None:
ifc_literals = cls.get_text_literal(obj, return_list=True)
for ifc_literal in ifc_literals or []:
if ifc_literal.is_a("IfcTextLiteralWithExtent"):
ifc_literal.BoxAlignment = alignment
# TODO below this point is highly experimental prototype code with no tests
@@ -2081,13 +2097,9 @@ class Drawing(bonsai.core.tool.Drawing):
cls,
text: str,
product: Optional[ifcopenshell.entity_instance] = None,
reverse_list: bool = False,
list_separator: str = ", ",
) -> str:
if not product:
return text
if list_separator:
list_separator = list_separator.encode().decode("unicode_escape")
for command in re.findall("``.*?``", text):
original_command = command
@@ -2102,10 +2114,7 @@ class Drawing(bonsai.core.tool.Drawing):
for variable in re.findall("{{.*?}}", text):
value = ifcopenshell.util.selector.get_element_value(product, variable[2:-2])
if isinstance(value, (list, tuple)):
if reverse_list:
value = list_separator.join(str(v) for v in reversed(value))
else:
value = list_separator.join(str(v) for v in value)
value = ", ".join(str(v) for v in value)
text = text.replace(variable, str(value))
return text