Add missing markDirty()/markCompositeDirty() to all Python property setters

Fixes a systemic bug where Python tp_getset property setters bypassed the
render cache dirty flag system (#144). The animation/C++ setProperty() path
had correct dirty propagation, but direct Python property assignments
(e.g. frame.x = 50, caption.text = "Hello") did not invalidate the parent
Frame's render cache when clip_children or cache_subtree was enabled.

Changes by file:
- UIDrawable.cpp: Add markCompositeDirty() to set_float_member (x/y),
  set_pos, set_grid_pos; add markDirty() for w/h resize
- UICaption.cpp: Add markDirty() to set_text, set_color_member,
  set_float_member (outline/font_size); markCompositeDirty() for position
- UICollection.cpp: Add markContentDirty() on owner in append, remove,
  pop, insert, extend, setitem, and slice assignment/deletion
- UISprite.cpp: Add markDirty() to scale/sprite_index/texture setters;
  markCompositeDirty() to position setters
- UICircle.cpp: Add markDirty() to radius/fill_color/outline_color/outline;
  markCompositeDirty() to center setter
- UILine.cpp: Add markDirty() to start/end/color/thickness setters
- UIArc.cpp: Add markDirty() to radius/angles/color/thickness setters;
  markCompositeDirty() to center setter
- UIGrid.cpp: Add markDirty() to center/zoom/camera_rotation/fill_color/
  size/perspective/fov setters

Closes #288, closes #289, closes #290, closes #291

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
John McCardle 2026-04-10 01:01:41 -04:00
commit e58b44ef82
9 changed files with 403 additions and 14 deletions

View file

@ -337,6 +337,7 @@ int UICircle::set_radius(PyUICircleObject* self, PyObject* value, void* closure)
return -1;
}
self->data->setRadius(static_cast<float>(PyFloat_AsDouble(value)));
self->data->markDirty(); // #291: visual change
return 0;
}
@ -355,6 +356,7 @@ int UICircle::set_center(PyUICircleObject* self, PyObject* value, void* closure)
return -1;
}
self->data->setCenter(vec->data);
self->data->markCompositeDirty(); // #291: position change
return 0;
}
@ -382,6 +384,7 @@ int UICircle::set_fill_color(PyUICircleObject* self, PyObject* value, void* clos
return -1;
}
self->data->setFillColor(color);
self->data->markDirty(); // #291: color change
return 0;
}
@ -409,6 +412,7 @@ int UICircle::set_outline_color(PyUICircleObject* self, PyObject* value, void* c
return -1;
}
self->data->setOutlineColor(color);
self->data->markDirty(); // #291: color change
return 0;
}
@ -422,6 +426,7 @@ int UICircle::set_outline(PyUICircleObject* self, PyObject* value, void* closure
return -1;
}
self->data->setOutline(static_cast<float>(PyFloat_AsDouble(value)));
self->data->markDirty(); // #291: visual change
return 0;
}