Delete-Cascade in EINEN Undo-Record — Cmd+Z stellt Wand + Volumen wieder her

Bug: Bei Wand-Loeschen lief das Delete von wand_axis als Rhino-Undo-Record A,
unsere Idle-Cascade (Volume-Delete, Oeffnungs-Cleanup) lief 500 ms spaeter
in separaten Records B/C/D. Cmd+Z popte nur den letzten Record → nur das
Volume kam zurueck, oder umgekehrt nur die Achse.

Fix: BeginUndoRecord("Element-Loeschen") in _on_command_begin fuer Delete-
Cmds. _flush_pending_cascades_sync laeuft in _on_command_end SOFORT (kein
500-ms-Wait noetig — bei explizitem Delete kommt die Source nicht zurueck)
und WAEHREND der Record offen ist. EndUndoRecord schliesst den Record.

Resultat: Rhinos User-Delete + alle Cascade-Deletes = EIN Cmd+Z.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 02:51:22 +02:00
parent 72e24fd512
commit 5fd6aefd34
+87 -1
View File
@@ -9519,6 +9519,65 @@ def _suppress_redraw_until_cmd_end():
print("[ELEMENTE] suppress redraw:", ex)
def _flush_pending_cascades_sync(doc):
"""Verarbeitet die wand_cascade- und source_cascade-Queues SOFORT
(ohne 500-ms-Wait). Aufgerufen aus _on_command_end fuer Delete-Cmds:
der User hat explizit geloescht, die Source kommt nicht zurueck
also kein Grund zu warten. Vorteil: laeuft noch im selben Undo-Record
wie Rhinos User-Delete ein Cmd+Z stellt alles wieder her.
"""
pending_cascade = sc.sticky.get("_elemente_pending_wand_cascade")
if isinstance(pending_cascade, dict) and pending_cascade:
to_run = [(wid, info.get("openings", []))
for wid, info in list(pending_cascade.items())]
pending_cascade.clear()
for wall_id, op_ids in to_run:
still_there = False
for obj in doc.Objects:
m = _read_meta(obj)
if m and m.get("id") == wall_id and m.get("type") == "wand_axis":
still_there = True
break
if still_there: continue
_was = sc.sticky.get(_REGEN_BUSY, False)
sc.sticky[_REGEN_BUSY] = True
try:
for op_id in op_ids:
for vol_obj in _find_all_volumes(doc, op_id):
try: doc.Objects.Delete(vol_obj.Id, True)
except Exception: pass
for obj, _m in _find_objects_by_wall_id(doc, op_id):
if _m.get("type") == "oeffnung_point":
try: doc.Objects.Delete(obj.Id, True)
except Exception: pass
finally:
sc.sticky[_REGEN_BUSY] = _was
pending_src = sc.sticky.get("_elemente_pending_source_cascade")
if isinstance(pending_src, dict) and pending_src:
to_run_src = list(pending_src.items())
pending_src.clear()
for src_id, info in to_run_src:
still_there = False
for obj in doc.Objects:
m = _read_meta(obj)
if m and m.get("id") == src_id and m.get("type") == info.get("type"):
still_there = True
break
if still_there: continue
_was = sc.sticky.get(_REGEN_BUSY, False)
sc.sticky[_REGEN_BUSY] = True
try:
for vol_id in info.get("volumes", []):
try: doc.Objects.Delete(vol_id, True)
except Exception: pass
finally:
sc.sticky[_REGEN_BUSY] = _was
parent_id = info.get("parent")
if parent_id:
_queue_regen(parent_id)
def _on_command_begin(sender, e):
try:
name = getattr(e, "CommandEnglishName", "") or ""
@@ -9541,6 +9600,16 @@ def _on_command_begin(sender, e):
sc.sticky["_dossier_bulk_redraw_prev"] = bool(doc.Views.RedrawEnabled)
doc.Views.RedrawEnabled = False
except Exception: pass
# Undo-Record umschliesst Rhinos User-Delete + unsere Cascade
# (Volume-Delete, Oeffnungs-Delete) in EINEM Cmd+Z-Schritt.
# Sonst bleibt nach Cmd+Z nur die Achse uebrig (Volumen kamen aus
# separatem Idle-Record und der hatte schon gepop't).
try:
serial = doc.BeginUndoRecord("Element-Loeschen")
sc.sticky["_dossier_bulk_undo_serial"] = serial
except Exception as ex:
print("[ELEMENTE] bulk undo record begin:", ex)
sc.sticky["_dossier_bulk_undo_serial"] = None
return
# Diagnose: andere Commands sehen wir hier vorbeiziehen — wenn _Delete
# einen anderen Namen hat als 'Delete', sehen wir's und koennen den
@@ -9570,9 +9639,26 @@ def _on_command_end(sender, e):
# refresh ans Gestaltung-Panel.
if sc.sticky.get(_BULK_ACTIVE_KEY):
sc.sticky[_BULK_ACTIVE_KEY] = None
doc = Rhino.RhinoDoc.ActiveDoc
# Cascade synchron flushen WAEHREND der Undo-Record noch offen ist
# und RedrawEnabled noch False ist (kein Flicker, ein Cmd+Z fuer
# alles). Nur fuer Delete-Cmds relevant — andere Bulk-Cmds queuen
# nichts.
try:
if doc is not None:
_flush_pending_cascades_sync(doc)
except Exception as ex:
print("[ELEMENTE] bulk cascade flush:", ex)
# Undo-Record schliessen — alles seit BeginUndoRecord (User-Delete +
# Cascade) wird als EIN Cmd+Z behandelt.
try:
serial = sc.sticky.pop("_dossier_bulk_undo_serial", None)
if serial is not None and doc is not None:
doc.EndUndoRecord(serial)
except Exception as ex:
print("[ELEMENTE] bulk undo record end:", ex)
try:
prev = sc.sticky.pop("_dossier_bulk_redraw_prev", True)
doc = Rhino.RhinoDoc.ActiveDoc
if doc is not None:
doc.Views.RedrawEnabled = prev
doc.Views.Redraw()