Wire array panel buttons to triad lifecycle

Two bugs in BIM_PT_array:

1. The "is this layer in edit mode" predicate compared a BoolProperty
   against an int (props.is_editing == i). Python evaluates False == 0
   as True, so layer 0 always rendered the per-layer edit form even
   when no edit was active — clicking validate/cancel then dispatched
   against a phantom edit state. Switched to
   props.editing_item_index == i, which defaults to -1 and matches
   exactly one layer when an edit is active.

2. The panel's CHECKMARK and CANCEL buttons called bim.edit_array /
   bim.disable_editing_array, a parallel lifecycle that only cleared
   editing_item_index. Entering edit mode via the viewport gizmo
   (bim.enable_editing_array, the triad enter) sets is_editing=True
   and hides array children; the legacy panel exit unwound neither —
   so committing or cancelling from the panel left is_editing=True
   with children hidden, and the viewport gizmo thought the edit was
   still in progress. Re-bound both panel buttons to the canonical
   triad operators (bim.finish_editing_array /
   bim.cancel_editing_array), which _ArrayEditMixin already owns and
   which the viewport gizmo group already uses. Panel and gizmo now
   share one exit path.

The three now-unreachable operators are deleted with their
registration entries: EditArray (bim.edit_array), DisableEditingArray
(bim.disable_editing_array), and EnableEditingArrayItem
(bim.enable_editing_array_item, never called from any UI). The two
test/tool/test_model.py sites that drove bim.edit_array as a commit
step are switched to bim.finish_editing_array.

External scripts or user keymaps bound to bim.edit_array /
bim.disable_editing_array will need to update — the replacements are
bim.finish_editing_array and bim.cancel_editing_array, both taking no
parameters (the layer is read from props.editing_item_index).

Partly generated with the assistance of an AI coding tool.
This commit is contained in:
Gorgious56
2026-06-02 10:37:01 +02:00
parent ba6cfe9c24
commit b7549f2476
4 changed files with 5 additions and 110 deletions
@@ -51,10 +51,7 @@ from . import (
classes = (
array.AddArray,
array.CancelEditingArray,
array.DisableEditingArray,
array.EditArray,
array.EnableEditingArray,
array.EnableEditingArrayItem,
array.FinishEditingArray,
array.ApplyArray,
array.RegenerateArray,
-102
View File
@@ -157,108 +157,6 @@ class AddArray(bpy.types.Operator, tool.Ifc.Operator):
tool.Array.constrain_children_to_parent(element)
class DisableEditingArray(bpy.types.Operator):
bl_idname = "bim.disable_editing_array"
bl_label = "Disable Editing Array"
bl_description = "Cancel editing this array without saving changes"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
obj = context.active_object
assert obj
tool.Model.get_array_props(obj).editing_item_index = -1
return {"FINISHED"}
class EnableEditingArrayItem(bpy.types.Operator):
"""Per-item array layer editing: hydrates props from one BBIM_Array layer.
The element-wide ``bim.enable_editing_array`` (parametric edit lifecycle) coexists with
this operator. They target different state: ``is_editing`` for the edit lifecycle,
``editing_item_index`` for the per-item panel UI."""
bl_idname = "bim.enable_editing_array_item"
bl_label = "Enable Editing Array Item"
bl_description = "Edit this array layer"
bl_options = {"REGISTER", "UNDO"}
item: bpy.props.IntProperty()
def execute(self, context):
obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj)
props = tool.Model.get_array_props(obj)
relating_obj = props.relating_array_object
if relating_obj:
element = tool.Ifc.get_entity(relating_obj)
parent_globalid = ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Parent")
parent_element = tool.Ifc.get().by_guid(parent_globalid)
data = json.loads(ifcopenshell.util.element.get_pset(parent_element, "BBIM_Array", "Data"))[self.item]
else:
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Data"))[self.item]
props.count = data["count"]
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
props.x = data["x"] * si_conversion
props.y = data["y"] * si_conversion
props.z = data["z"] * si_conversion
props.use_local_space = data.get("use_local_space", False)
props.method = data.get("method", "OFFSET")
props.per_child_opening = data.get("per_child_opening", data.get("mirror_to_host", True))
props.editing_item_index = self.item
return {"FINISHED"}
class EditArray(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_array"
bl_label = "Edit Array"
bl_description = "Save changes to this array layer"
bl_options = {"REGISTER", "UNDO"}
item: bpy.props.IntProperty()
def _execute(self, context):
obj = context.active_object
element = tool.Ifc.get_entity(obj)
props = tool.Model.get_array_props(obj)
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
data = json.loads(pset["Data"])
data[self.item] = {
"children": data[self.item]["children"],
"count": props.count,
"x": props.x / si_conversion,
"y": props.y / si_conversion,
"z": props.z / si_conversion,
"use_local_space": props.use_local_space,
"method": props.method,
"per_child_opening": props.per_child_opening,
}
props.editing_item_index = -1
try:
parent_element = tool.Ifc.get().by_guid(pset["Parent"])
parent = tool.Ifc.get_object(parent_element)
except:
return {"FINISHED"}
tool.Array.remove_constraints(parent_element)
# Conditional wipe-and-rebuild — only when the parent's geometry
# differs from the children's. See ``_parent_geometry_changed`` for
# the bbox-dim heuristic and its known false-negative case.
if _parent_geometry_changed(parent, data):
_wipe_array_children(data)
tool.Model.regenerate_array(parent, data)
tool.Array.set_children_lock_state(element, self.item, True)
tool.Array.constrain_children_to_parent(element)
# clears the relating_array_object so it doesn't show again next time
props.relating_array_object = None
class _ArrayEditMixin(ParametricEditMixinBase):
"""Array edit lifecycle scoped to one layer at a time.
+3 -3
View File
@@ -238,11 +238,11 @@ class BIM_PT_array(bpy.types.Panel):
for i, array in enumerate(ArrayData.data["parameters"]["data_dict"]):
box = self.layout.box()
if props.is_editing == i:
if props.editing_item_index == i:
row = box.row(align=True)
row.prop(props, "count", icon="MOD_ARRAY")
row.operator("bim.edit_array", icon="CHECKMARK", text="").item = i
row.operator("bim.disable_editing_array", icon="CANCEL", text="")
row.operator("bim.finish_editing_array", icon="CHECKMARK", text="")
row.operator("bim.cancel_editing_array", icon="CANCEL", text="")
row = box.row(align=True)
row.prop(props, "method")
row = box.row(align=True)
+2 -2
View File
@@ -605,7 +605,7 @@ class TestUsingArrays(NewFile):
props.count = 4
props.x = 4
props.sync_children = sync_children
bpy.ops.bim.edit_array(item=0)
bpy.ops.bim.finish_editing_array()
if add_second_layer:
bpy.ops.bim.add_array()
@@ -614,7 +614,7 @@ class TestUsingArrays(NewFile):
props.count = 3
props.y = 4
props.sync_children = sync_children
bpy.ops.bim.edit_array(item=1)
bpy.ops.bim.finish_editing_array()
def test_remove_array_last_to_first(self):
self.setup_array(add_second_layer=True)