mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-08 08:51:35 +00:00
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:
@@ -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"}
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user