mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 23:36:20 +00:00
Supersede 3x3 box alignment with more familiar horizontal / vertical UI
* Fix #7712 - global alignment controls now affects all literals * Fix #7760 - goodbye 3x3 box alignment Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3305,9 +3305,8 @@ class AddTextLiteral(bpy.types.Operator):
|
|||||||
attr.data_type = "string"
|
attr.data_type = "string"
|
||||||
attr.string_value = literal_attr_values[attr_name]
|
attr.string_value = literal_attr_values[attr_name]
|
||||||
|
|
||||||
box_alignment_mask = [False] * 9
|
literal_props.align_vertical = "bottom"
|
||||||
box_alignment_mask[6] = True # bottom_left box_alignment
|
literal_props.align_horizontal = "left"
|
||||||
literal_props.box_alignment = box_alignment_mask
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -4178,10 +4177,7 @@ class SelectSimilarTextLiteralValue(bpy.types.Operator):
|
|||||||
should_select = True
|
should_select = True
|
||||||
break
|
break
|
||||||
elif self.attribute_type == "box_alignment":
|
elif self.attribute_type == "box_alignment":
|
||||||
box_alignment_attr = next(
|
if literal.get_box_alignment() == self.literal_value:
|
||||||
(attr for attr in literal.attributes if attr.name == "BoxAlignment"), None
|
|
||||||
)
|
|
||||||
if box_alignment_attr and box_alignment_attr.string_value == self.literal_value:
|
|
||||||
should_select = True
|
should_select = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
@@ -673,20 +673,6 @@ class BIMCameraProperties(PropertyGroup):
|
|||||||
return ortho_scale, aspect_ratio
|
return ortho_scale, aspect_ratio
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_BOX_ALIGNMENT = [False] * 6 + [True] + [False] * 2
|
|
||||||
BOX_ALIGNMENT_POSITIONS = [
|
|
||||||
"top-left",
|
|
||||||
"top-middle",
|
|
||||||
"top-right",
|
|
||||||
"middle-left",
|
|
||||||
"center",
|
|
||||||
"middle-right",
|
|
||||||
"bottom-left",
|
|
||||||
"bottom-middle",
|
|
||||||
"bottom-right",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ElementValueRow(PropertyGroup):
|
class ElementValueRow(PropertyGroup):
|
||||||
"""Represents a single element value row with category, key, and formatted value"""
|
"""Represents a single element value row with category, key, and formatted value"""
|
||||||
|
|
||||||
@@ -789,40 +775,38 @@ def get_category_items_with_counts(self, context):
|
|||||||
|
|
||||||
|
|
||||||
class LiteralProps(PropertyGroup):
|
class LiteralProps(PropertyGroup):
|
||||||
def set_box_alignment(self, new_value):
|
|
||||||
markers = new_value.count(True)
|
|
||||||
if not markers:
|
|
||||||
return
|
|
||||||
|
|
||||||
if markers > 1:
|
|
||||||
prev_value = self.get("box_alignment", DEFAULT_BOX_ALIGNMENT)
|
|
||||||
# looking for the first value changed to positive
|
|
||||||
first_changed_value = next((i for i in range(9) if new_value[i] and new_value[i] != prev_value[i]), None)
|
|
||||||
|
|
||||||
# if nothing have changed we just keep the previous value
|
|
||||||
if first_changed_value is None:
|
|
||||||
return
|
|
||||||
new_value = [False] * 9
|
|
||||||
new_value[first_changed_value] = True
|
|
||||||
|
|
||||||
self["box_alignment"] = new_value
|
|
||||||
position_string = BOX_ALIGNMENT_POSITIONS[next(i for i in range(9) if new_value[i])]
|
|
||||||
self.attributes["BoxAlignment"].set_value(position_string)
|
|
||||||
|
|
||||||
def get_box_alignment(self):
|
|
||||||
return self.get("box_alignment", DEFAULT_BOX_ALIGNMENT)
|
|
||||||
|
|
||||||
attributes: CollectionProperty(name="Attributes", type=Attribute)
|
attributes: CollectionProperty(name="Attributes", type=Attribute)
|
||||||
box_alignment: BoolVectorProperty(
|
|
||||||
name="Box alignment", size=9, set=set_box_alignment, get=get_box_alignment, default=DEFAULT_BOX_ALIGNMENT
|
|
||||||
)
|
|
||||||
ifc_definition_id: IntProperty(name="IFC definition ID", default=0)
|
ifc_definition_id: IntProperty(name="IFC definition ID", default=0)
|
||||||
|
align_horizontal: EnumProperty(
|
||||||
|
items=[
|
||||||
|
("left", "Left", "", "ALIGN_LEFT", 0),
|
||||||
|
("middle", "Middle", "", "ALIGN_CENTER", 1),
|
||||||
|
("right", "Right", "", "ALIGN_RIGHT", 2),
|
||||||
|
],
|
||||||
|
default="left",
|
||||||
|
name="Horizontal Alignment",
|
||||||
|
)
|
||||||
|
align_vertical: EnumProperty(
|
||||||
|
items=[
|
||||||
|
("top", "Top", "", "ALIGN_TOP", 0),
|
||||||
|
("middle", "Middle", "", "ALIGN_MIDDLE", 1),
|
||||||
|
("bottom", "Bottom", "", "ALIGN_BOTTOM", 2),
|
||||||
|
],
|
||||||
|
default="middle",
|
||||||
|
name="Vertical Alignment",
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_box_alignment(self) -> str:
|
||||||
|
alignment = self.align_vertical + "-" + self.align_horizontal
|
||||||
|
if alignment == "middle-middle":
|
||||||
|
alignment = "center"
|
||||||
|
return alignment
|
||||||
|
|
||||||
def get_literal_edited_data(self) -> dict[str, str]:
|
def get_literal_edited_data(self) -> dict[str, str]:
|
||||||
text_data = {
|
text_data = {
|
||||||
"CurrentValue": self.attributes["Literal"].string_value,
|
"CurrentValue": self.attributes["Literal"].string_value,
|
||||||
"Literal": self.attributes["Literal"].string_value,
|
"Literal": self.attributes["Literal"].string_value,
|
||||||
"BoxAlignment": self.attributes["BoxAlignment"].string_value,
|
"BoxAlignment": self.get_box_alignment(),
|
||||||
}
|
}
|
||||||
return text_data
|
return text_data
|
||||||
|
|
||||||
@@ -860,12 +844,19 @@ class LiteralProps(PropertyGroup):
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||||
value: str
|
value: str
|
||||||
box_alignment: tuple[bool, bool, bool, bool, bool, bool, bool, bool, bool]
|
|
||||||
ifc_definition_id: int
|
ifc_definition_id: int
|
||||||
|
align_horizontal: str
|
||||||
|
align_vertical: str
|
||||||
element_value_rows: bpy.types.bpy_prop_collection_idprop[ElementValueRow]
|
element_value_rows: bpy.types.bpy_prop_collection_idprop[ElementValueRow]
|
||||||
category_for_adding: str
|
category_for_adding: str
|
||||||
|
|
||||||
|
|
||||||
|
def update_text_alignment(self, context):
|
||||||
|
for literal_props in self.literals:
|
||||||
|
literal_props.align_horizontal = self.align_horizontal
|
||||||
|
literal_props.align_vertical = self.align_vertical
|
||||||
|
|
||||||
|
|
||||||
class BIMTextProperties(PropertyGroup):
|
class BIMTextProperties(PropertyGroup):
|
||||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||||
literals: CollectionProperty(name="Literals", type=LiteralProps)
|
literals: CollectionProperty(name="Literals", type=LiteralProps)
|
||||||
@@ -899,6 +890,7 @@ class BIMTextProperties(PropertyGroup):
|
|||||||
],
|
],
|
||||||
default="left",
|
default="left",
|
||||||
name="Horizontal Alignment",
|
name="Horizontal Alignment",
|
||||||
|
update=update_text_alignment,
|
||||||
)
|
)
|
||||||
align_vertical: EnumProperty(
|
align_vertical: EnumProperty(
|
||||||
items=[
|
items=[
|
||||||
@@ -908,6 +900,7 @@ class BIMTextProperties(PropertyGroup):
|
|||||||
],
|
],
|
||||||
default="middle",
|
default="middle",
|
||||||
name="Vertical Alignment",
|
name="Vertical Alignment",
|
||||||
|
update=update_text_alignment,
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|||||||
@@ -781,33 +781,10 @@ class BIM_PT_text(Panel):
|
|||||||
if other_attributes:
|
if other_attributes:
|
||||||
bonsai.bim.helper.draw_attributes(other_attributes, box)
|
bonsai.bim.helper.draw_attributes(other_attributes, box)
|
||||||
|
|
||||||
row = box.row(align=True)
|
row = box.row()
|
||||||
cols = [row.column(align=True) for j in range(3)]
|
row.label(text="Alignment")
|
||||||
for j in range(9):
|
row.prop(literal_props, "align_horizontal", text="", expand=True)
|
||||||
cols[j % 3].prop(
|
row.prop(literal_props, "align_vertical", text="", expand=True)
|
||||||
literal_props,
|
|
||||||
"box_alignment",
|
|
||||||
text="",
|
|
||||||
index=j,
|
|
||||||
icon="RADIOBUT_ON" if literal_props.box_alignment[j] else "RADIOBUT_OFF",
|
|
||||||
)
|
|
||||||
|
|
||||||
col = row.column(align=True)
|
|
||||||
alignment_label_row = col.row(align=True)
|
|
||||||
alignment_label_row.label(text=" Text box alignment:")
|
|
||||||
|
|
||||||
box_alignment_value = (
|
|
||||||
literal_props.attributes[
|
|
||||||
next(
|
|
||||||
(idx for idx, attr in enumerate(literal_props.attributes) if attr.name == "BoxAlignment"),
|
|
||||||
-1,
|
|
||||||
)
|
|
||||||
].string_value
|
|
||||||
if any(attr.name == "BoxAlignment" for attr in literal_props.attributes)
|
|
||||||
else "N/A"
|
|
||||||
)
|
|
||||||
|
|
||||||
col.label(text=f" {box_alignment_value}")
|
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
obj = context.active_object
|
obj = context.active_object
|
||||||
|
|||||||
@@ -603,6 +603,10 @@ class Drawing(bonsai.core.tool.Drawing):
|
|||||||
props = tool.Drawing.get_text_props(obj)
|
props = tool.Drawing.get_text_props(obj)
|
||||||
for literal_props in props.literals:
|
for literal_props in props.literals:
|
||||||
literal_data = bonsai.bim.helper.export_attributes(literal_props.attributes)
|
literal_data = bonsai.bim.helper.export_attributes(literal_props.attributes)
|
||||||
|
alignment = literal_props.align_vertical + "-" + literal_props.align_horizontal
|
||||||
|
if alignment == "middle-middle":
|
||||||
|
alignment = "center"
|
||||||
|
literal_data["BoxAlignment"] = alignment
|
||||||
literals.append(literal_data)
|
literals.append(literal_data)
|
||||||
return literals
|
return literals
|
||||||
|
|
||||||
@@ -1176,22 +1180,26 @@ class Drawing(bonsai.core.tool.Drawing):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def import_text_attributes(cls, obj: bpy.types.Object) -> None:
|
def import_text_attributes(cls, obj: bpy.types.Object) -> None:
|
||||||
from bonsai.bim.module.drawing.prop import BOX_ALIGNMENT_POSITIONS
|
|
||||||
|
|
||||||
props = cls.get_text_props(obj)
|
props = cls.get_text_props(obj)
|
||||||
props.literals.clear()
|
props.literals.clear()
|
||||||
|
|
||||||
ifc_literals = cls.get_text_literal(obj, return_list=True)
|
ifc_literals = cls.get_text_literal(obj, return_list=True)
|
||||||
assert isinstance(ifc_literals, list)
|
assert isinstance(ifc_literals, list)
|
||||||
|
|
||||||
|
if ifc_literals:
|
||||||
|
first_alignment = getattr(ifc_literals[0], "BoxAlignment", None) or "bottom-left"
|
||||||
|
if first_alignment == "center":
|
||||||
|
first_alignment = "middle-middle"
|
||||||
|
props.align_vertical, props.align_horizontal = first_alignment.split("-")
|
||||||
|
|
||||||
for ifc_literal in ifc_literals:
|
for ifc_literal in ifc_literals:
|
||||||
literal_props = props.literals.add()
|
literal_props = props.literals.add()
|
||||||
bonsai.bim.helper.import_attributes(ifc_literal, literal_props.attributes)
|
bonsai.bim.helper.import_attributes(ifc_literal, literal_props.attributes)
|
||||||
|
|
||||||
box_alignment_mask = [False] * 9
|
alignment = getattr(ifc_literal, "BoxAlignment", None) or "bottom-left"
|
||||||
position_string = literal_props.attributes["BoxAlignment"].string_value
|
if alignment == "center":
|
||||||
box_alignment_mask[BOX_ALIGNMENT_POSITIONS.index(position_string)] = True
|
alignment = "middle-middle"
|
||||||
|
literal_props.align_vertical, literal_props.align_horizontal = alignment.split("-")
|
||||||
literal_props.box_alignment = box_alignment_mask # pyright: ignore[reportAttributeAccessIssue]
|
|
||||||
literal_props.ifc_definition_id = ifc_literal.id()
|
literal_props.ifc_definition_id = ifc_literal.id()
|
||||||
|
|
||||||
from bonsai.bim.module.drawing.data import DecoratorData
|
from bonsai.bim.module.drawing.data import DecoratorData
|
||||||
|
|||||||
@@ -666,10 +666,13 @@ class TestImportTextAttributes(NewFile):
|
|||||||
|
|
||||||
literal_props = props.literals[0]
|
literal_props = props.literals[0]
|
||||||
assert literal_props.ifc_definition_id == item.id()
|
assert literal_props.ifc_definition_id == item.id()
|
||||||
assert literal_props.box_alignment[:] == tuple([False] * 6 + [True] + [False] * 2)
|
|
||||||
assert literal_props.attributes["Literal"].string_value == "Literal"
|
assert literal_props.attributes["Literal"].string_value == "Literal"
|
||||||
assert literal_props.attributes["Path"].enum_value == "RIGHT"
|
assert literal_props.attributes["Path"].enum_value == "RIGHT"
|
||||||
assert literal_props.attributes["BoxAlignment"].string_value == "bottom-left"
|
assert literal_props.attributes["BoxAlignment"].string_value == "bottom-left"
|
||||||
|
assert literal_props.align_vertical == "bottom"
|
||||||
|
assert literal_props.align_horizontal == "left"
|
||||||
|
assert props.align_vertical == "bottom"
|
||||||
|
assert props.align_horizontal == "left"
|
||||||
|
|
||||||
|
|
||||||
class TestReplaceTextLiteralVariables(NewFile):
|
class TestReplaceTextLiteralVariables(NewFile):
|
||||||
|
|||||||
Reference in New Issue
Block a user