Replace PyObject_GetAttrString with direct type references
Replace ~230 occurrences of PyObject_GetAttrString(McRFPy_API::mcrf_module, "TypeName") with direct &mcrfpydef::PyXxxType references across 32 source files. Each PyObject_GetAttrString call returns a new reference. When used inline in PyObject_IsInstance(), that reference was immediately leaked. When used for tp_alloc, the reference required careful Py_DECREF management that was often missing on error paths. Direct type references are compile-time constants that never need reference counting, eliminating ~230 potential leak sites and removing ~100 lines of Py_DECREF/Py_XDECREF cleanup code. Also adds extractDrawable() helper in UICollection.cpp to replace repeated 8-way type-check-and-extract chains with a single function call. Closes #267, closes #268 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
348826a0f5
commit
71eb01c950
32 changed files with 249 additions and 944 deletions
|
|
@ -20,7 +20,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
|
|||
if (!drawable) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
// Check cache first
|
||||
if (drawable->serial_number != 0) {
|
||||
PyObject* cached = PythonObjectCache::getInstance().lookup(drawable->serial_number);
|
||||
|
|
@ -28,15 +28,14 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
|
|||
return cached; // Already INCREF'd by lookup
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PyTypeObject* type = nullptr;
|
||||
PyObject* obj = nullptr;
|
||||
|
||||
|
||||
switch (drawable->derived_type()) {
|
||||
case PyObjectsEnum::UIFRAME:
|
||||
{
|
||||
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame");
|
||||
if (!type) return nullptr;
|
||||
type = &PyUIFrameType;
|
||||
auto pyObj = (PyUIFrameObject*)type->tp_alloc(type, 0);
|
||||
if (pyObj) {
|
||||
pyObj->data = std::static_pointer_cast<UIFrame>(drawable);
|
||||
|
|
@ -47,8 +46,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
|
|||
}
|
||||
case PyObjectsEnum::UICAPTION:
|
||||
{
|
||||
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption");
|
||||
if (!type) return nullptr;
|
||||
type = &PyUICaptionType;
|
||||
auto pyObj = (PyUICaptionObject*)type->tp_alloc(type, 0);
|
||||
if (pyObj) {
|
||||
pyObj->data = std::static_pointer_cast<UICaption>(drawable);
|
||||
|
|
@ -60,8 +58,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
|
|||
}
|
||||
case PyObjectsEnum::UISPRITE:
|
||||
{
|
||||
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite");
|
||||
if (!type) return nullptr;
|
||||
type = &PyUISpriteType;
|
||||
auto pyObj = (PyUISpriteObject*)type->tp_alloc(type, 0);
|
||||
if (pyObj) {
|
||||
pyObj->data = std::static_pointer_cast<UISprite>(drawable);
|
||||
|
|
@ -72,8 +69,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
|
|||
}
|
||||
case PyObjectsEnum::UIGRID:
|
||||
{
|
||||
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid");
|
||||
if (!type) return nullptr;
|
||||
type = &PyUIGridType;
|
||||
auto pyObj = (PyUIGridObject*)type->tp_alloc(type, 0);
|
||||
if (pyObj) {
|
||||
pyObj->data = std::static_pointer_cast<UIGrid>(drawable);
|
||||
|
|
@ -84,8 +80,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
|
|||
}
|
||||
case PyObjectsEnum::UILINE:
|
||||
{
|
||||
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line");
|
||||
if (!type) return nullptr;
|
||||
type = &PyUILineType;
|
||||
auto pyObj = (PyUILineObject*)type->tp_alloc(type, 0);
|
||||
if (pyObj) {
|
||||
pyObj->data = std::static_pointer_cast<UILine>(drawable);
|
||||
|
|
@ -96,8 +91,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
|
|||
}
|
||||
case PyObjectsEnum::UICIRCLE:
|
||||
{
|
||||
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle");
|
||||
if (!type) return nullptr;
|
||||
type = &PyUICircleType;
|
||||
auto pyObj = (PyUICircleObject*)type->tp_alloc(type, 0);
|
||||
if (pyObj) {
|
||||
pyObj->data = std::static_pointer_cast<UICircle>(drawable);
|
||||
|
|
@ -108,8 +102,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
|
|||
}
|
||||
case PyObjectsEnum::UIARC:
|
||||
{
|
||||
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc");
|
||||
if (!type) return nullptr;
|
||||
type = &PyUIArcType;
|
||||
auto pyObj = (PyUIArcObject*)type->tp_alloc(type, 0);
|
||||
if (pyObj) {
|
||||
pyObj->data = std::static_pointer_cast<UIArc>(drawable);
|
||||
|
|
@ -120,8 +113,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
|
|||
}
|
||||
case PyObjectsEnum::UIVIEWPORT3D:
|
||||
{
|
||||
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Viewport3D");
|
||||
if (!type) return nullptr;
|
||||
type = &PyViewport3DType;
|
||||
auto pyObj = (PyViewport3DObject*)type->tp_alloc(type, 0);
|
||||
if (pyObj) {
|
||||
pyObj->data = std::static_pointer_cast<mcrf::Viewport3D>(drawable);
|
||||
|
|
@ -134,10 +126,6 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
|
|||
PyErr_SetString(PyExc_TypeError, "Unknown UIDrawable derived type");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type) {
|
||||
Py_DECREF(type);
|
||||
}
|
||||
|
||||
// Re-register in cache if the object has a serial number
|
||||
// This handles the case where the original Python wrapper was GC'd
|
||||
|
|
@ -153,6 +141,28 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
|
|||
return obj;
|
||||
}
|
||||
|
||||
// Helper to extract shared_ptr<UIDrawable> from any UIDrawable Python subclass.
|
||||
// Returns nullptr without setting an error if the type doesn't match.
|
||||
static std::shared_ptr<UIDrawable> extractDrawable(PyObject* o) {
|
||||
if (PyObject_IsInstance(o, (PyObject*)&PyUIFrameType))
|
||||
return ((PyUIFrameObject*)o)->data;
|
||||
if (PyObject_IsInstance(o, (PyObject*)&PyUICaptionType))
|
||||
return ((PyUICaptionObject*)o)->data;
|
||||
if (PyObject_IsInstance(o, (PyObject*)&PyUISpriteType))
|
||||
return ((PyUISpriteObject*)o)->data;
|
||||
if (PyObject_IsInstance(o, (PyObject*)&PyUIGridType))
|
||||
return ((PyUIGridObject*)o)->data;
|
||||
if (PyObject_IsInstance(o, (PyObject*)&PyUILineType))
|
||||
return ((PyUILineObject*)o)->data;
|
||||
if (PyObject_IsInstance(o, (PyObject*)&PyUICircleType))
|
||||
return ((PyUICircleObject*)o)->data;
|
||||
if (PyObject_IsInstance(o, (PyObject*)&PyUIArcType))
|
||||
return ((PyUIArcObject*)o)->data;
|
||||
if (PyObject_IsInstance(o, (PyObject*)&PyViewport3DType))
|
||||
return ((PyViewport3DObject*)o)->data;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int UICollectionIter::init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
|
||||
|
|
@ -250,37 +260,13 @@ int UICollection::setitem(PyUICollectionObject* self, Py_ssize_t index, PyObject
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Type checking - must be a UIDrawable subclass
|
||||
if (!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) &&
|
||||
!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) &&
|
||||
!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) &&
|
||||
!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
PyErr_SetString(PyExc_TypeError, "UICollection can only contain Frame, Caption, Sprite, and Grid objects");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the C++ object from the Python object
|
||||
std::shared_ptr<UIDrawable> new_drawable = nullptr;
|
||||
int old_z_index = (*vec)[index]->z_index; // Preserve the z_index
|
||||
|
||||
if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) {
|
||||
PyUIFrameObject* frame = (PyUIFrameObject*)value;
|
||||
new_drawable = frame->data;
|
||||
} else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) {
|
||||
PyUICaptionObject* caption = (PyUICaptionObject*)value;
|
||||
new_drawable = caption->data;
|
||||
} else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) {
|
||||
PyUISpriteObject* sprite = (PyUISpriteObject*)value;
|
||||
new_drawable = sprite->data;
|
||||
} else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
PyUIGridObject* grid = (PyUIGridObject*)value;
|
||||
new_drawable = grid->data;
|
||||
}
|
||||
|
||||
// Extract drawable from Python object (type-checks internally)
|
||||
std::shared_ptr<UIDrawable> new_drawable = extractDrawable(value);
|
||||
if (!new_drawable) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Failed to extract C++ object from Python object");
|
||||
PyErr_SetString(PyExc_TypeError, "UICollection can only contain Drawable objects");
|
||||
return -1;
|
||||
}
|
||||
int old_z_index = (*vec)[index]->z_index; // Preserve the z_index
|
||||
|
||||
// #122: Clear parent of old element
|
||||
(*vec)[index]->setParent(nullptr);
|
||||
|
|
@ -312,32 +298,8 @@ int UICollection::contains(PyUICollectionObject* self, PyObject* value) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
// Type checking - must be a UIDrawable subclass
|
||||
if (!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) &&
|
||||
!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) &&
|
||||
!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) &&
|
||||
!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
// Not a valid type, so it can't be in the collection
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get the C++ object from the Python object
|
||||
std::shared_ptr<UIDrawable> search_drawable = nullptr;
|
||||
|
||||
if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) {
|
||||
PyUIFrameObject* frame = (PyUIFrameObject*)value;
|
||||
search_drawable = frame->data;
|
||||
} else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) {
|
||||
PyUICaptionObject* caption = (PyUICaptionObject*)value;
|
||||
search_drawable = caption->data;
|
||||
} else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) {
|
||||
PyUISpriteObject* sprite = (PyUISpriteObject*)value;
|
||||
search_drawable = sprite->data;
|
||||
} else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
PyUIGridObject* grid = (PyUIGridObject*)value;
|
||||
search_drawable = grid->data;
|
||||
}
|
||||
|
||||
// Extract drawable (returns nullptr for non-drawable types)
|
||||
std::shared_ptr<UIDrawable> search_drawable = extractDrawable(value);
|
||||
if (!search_drawable) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -412,14 +374,11 @@ PyObject* UICollection::inplace_concat(PyUICollectionObject* self, PyObject* oth
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// Type check
|
||||
if (!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) &&
|
||||
!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) &&
|
||||
!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) &&
|
||||
!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
// Type check - must be a UIDrawable subclass
|
||||
if (!PyObject_IsInstance(item, (PyObject*)&PyDrawableType)) {
|
||||
Py_DECREF(item);
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"UICollection can only contain Frame, Caption, Sprite, and Grid objects; "
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"UICollection can only contain Drawable objects; "
|
||||
"got %s at index %zd", Py_TYPE(item)->tp_name, i);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -544,20 +503,11 @@ int UICollection::ass_subscript(PyUICollectionObject* self, PyObject* key, PyObj
|
|||
}
|
||||
|
||||
// Type check and extract C++ object
|
||||
std::shared_ptr<UIDrawable> drawable = nullptr;
|
||||
|
||||
if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) {
|
||||
drawable = ((PyUIFrameObject*)item)->data;
|
||||
} else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) {
|
||||
drawable = ((PyUICaptionObject*)item)->data;
|
||||
} else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) {
|
||||
drawable = ((PyUISpriteObject*)item)->data;
|
||||
} else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
drawable = ((PyUIGridObject*)item)->data;
|
||||
} else {
|
||||
std::shared_ptr<UIDrawable> drawable = extractDrawable(item);
|
||||
if (!drawable) {
|
||||
Py_DECREF(item);
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"UICollection can only contain Frame, Caption, Sprite, and Grid objects; "
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"UICollection can only contain Drawable objects; "
|
||||
"got %s at index %zd", Py_TYPE(item)->tp_name, i);
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -629,45 +579,22 @@ PySequenceMethods UICollection::sqmethods = {
|
|||
.sq_inplace_repeat = NULL
|
||||
};
|
||||
|
||||
/* Idiomatic way to fetch complete types from the API rather than referencing their PyTypeObject struct
|
||||
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture");
|
||||
|
||||
I never identified why `using namespace mcrfpydef;` doesn't solve the segfault issue.
|
||||
The horrible macro in UIDrawable was originally a workaround for this, but as I interact with the types outside of the monster UI.h, a more general (and less icky) solution is required.
|
||||
|
||||
*/
|
||||
|
||||
PyObject* UICollection::append(PyUICollectionObject* self, PyObject* o)
|
||||
{
|
||||
// if not UIDrawable subclass, reject it
|
||||
// self->data->push_back( c++ object inside o );
|
||||
|
||||
// Ensure module is initialized
|
||||
if (!McRFPy_API::mcrf_module) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "mcrfpy module not initialized");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// this would be a great use case for .tp_base
|
||||
if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) &&
|
||||
!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) &&
|
||||
!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) &&
|
||||
!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid")) &&
|
||||
!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line")) &&
|
||||
!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle")) &&
|
||||
!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc")) &&
|
||||
!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Viewport3D"))
|
||||
)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, Grid, Line, Circle, Arc, and Viewport3D objects can be added to UICollection");
|
||||
// Extract drawable from Python object
|
||||
std::shared_ptr<UIDrawable> drawable = extractDrawable(o);
|
||||
if (!drawable) {
|
||||
PyErr_SetString(PyExc_TypeError, "Only Drawable objects can be added to UICollection");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Calculate z_index for the new element
|
||||
int new_z_index = 0;
|
||||
if (!self->data->empty()) {
|
||||
// Get the z_index of the last element and add 10
|
||||
int last_z = self->data->back()->z_index;
|
||||
if (last_z <= INT_MAX - 10) {
|
||||
new_z_index = last_z + 10;
|
||||
|
|
@ -676,60 +603,20 @@ PyObject* UICollection::append(PyUICollectionObject* self, PyObject* o)
|
|||
}
|
||||
}
|
||||
|
||||
// #122: Get the owner as parent for this drawable
|
||||
std::shared_ptr<UIDrawable> owner_ptr = self->owner.lock();
|
||||
// #183: Remove from old parent (drawable or scene)
|
||||
drawable->removeFromParent();
|
||||
|
||||
// Helper lambda to add drawable with parent tracking
|
||||
auto addDrawable = [&](std::shared_ptr<UIDrawable> drawable) {
|
||||
// #183: Remove from old parent (drawable or scene)
|
||||
drawable->removeFromParent();
|
||||
drawable->z_index = new_z_index;
|
||||
|
||||
drawable->z_index = new_z_index;
|
||||
// #183: Set new parent - either scene or drawable
|
||||
if (!self->scene_name.empty()) {
|
||||
drawable->setParentScene(self->scene_name);
|
||||
} else {
|
||||
std::shared_ptr<UIDrawable> owner_ptr = self->owner.lock();
|
||||
drawable->setParent(owner_ptr);
|
||||
}
|
||||
|
||||
// #183: Set new parent - either scene or drawable
|
||||
if (!self->scene_name.empty()) {
|
||||
// This is a scene's children collection
|
||||
drawable->setParentScene(self->scene_name);
|
||||
} else {
|
||||
// This is a Frame/Grid's children collection
|
||||
drawable->setParent(owner_ptr);
|
||||
}
|
||||
|
||||
self->data->push_back(drawable);
|
||||
};
|
||||
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")))
|
||||
{
|
||||
addDrawable(((PyUIFrameObject*)o)->data);
|
||||
}
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")))
|
||||
{
|
||||
addDrawable(((PyUICaptionObject*)o)->data);
|
||||
}
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")))
|
||||
{
|
||||
addDrawable(((PyUISpriteObject*)o)->data);
|
||||
}
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid")))
|
||||
{
|
||||
addDrawable(((PyUIGridObject*)o)->data);
|
||||
}
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line")))
|
||||
{
|
||||
addDrawable(((PyUILineObject*)o)->data);
|
||||
}
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle")))
|
||||
{
|
||||
addDrawable(((PyUICircleObject*)o)->data);
|
||||
}
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc")))
|
||||
{
|
||||
addDrawable(((PyUIArcObject*)o)->data);
|
||||
}
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Viewport3D")))
|
||||
{
|
||||
addDrawable(((PyViewport3DObject*)o)->data);
|
||||
}
|
||||
self->data->push_back(drawable);
|
||||
|
||||
// Mark scene as needing resort after adding element
|
||||
McRFPy_API::markSceneNeedsSort();
|
||||
|
|
@ -747,83 +634,34 @@ PyObject* UICollection::extend(PyUICollectionObject* self, PyObject* iterable)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// Ensure module is initialized
|
||||
if (!McRFPy_API::mcrf_module) {
|
||||
Py_DECREF(iterator);
|
||||
PyErr_SetString(PyExc_RuntimeError, "mcrfpy module not initialized");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get current highest z_index
|
||||
int current_z_index = 0;
|
||||
if (!self->data->empty()) {
|
||||
current_z_index = self->data->back()->z_index;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<UIDrawable> owner_ptr = self->owner.lock();
|
||||
|
||||
PyObject* item;
|
||||
while ((item = PyIter_Next(iterator)) != NULL) {
|
||||
// Check if item is a UIDrawable subclass
|
||||
if (!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) &&
|
||||
!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) &&
|
||||
!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) &&
|
||||
!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid")) &&
|
||||
!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line")) &&
|
||||
!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle")) &&
|
||||
!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc")) &&
|
||||
!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Viewport3D")))
|
||||
{
|
||||
std::shared_ptr<UIDrawable> drawable = extractDrawable(item);
|
||||
if (!drawable) {
|
||||
Py_DECREF(item);
|
||||
Py_DECREF(iterator);
|
||||
PyErr_SetString(PyExc_TypeError, "All items must be Frame, Caption, Sprite, Grid, Line, Circle, Arc, or Viewport3D objects");
|
||||
PyErr_SetString(PyExc_TypeError, "All items must be Drawable objects");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Increment z_index for each new element
|
||||
|
||||
if (current_z_index <= INT_MAX - 10) {
|
||||
current_z_index += 10;
|
||||
} else {
|
||||
current_z_index = INT_MAX;
|
||||
}
|
||||
|
||||
// #122: Get the owner as parent for this drawable
|
||||
std::shared_ptr<UIDrawable> owner_ptr = self->owner.lock();
|
||||
|
||||
// Helper lambda to add drawable with parent tracking
|
||||
auto addDrawable = [&](std::shared_ptr<UIDrawable> drawable) {
|
||||
// #122: Remove from old parent if it has one
|
||||
if (auto old_parent = drawable->getParent()) {
|
||||
drawable->removeFromParent();
|
||||
}
|
||||
drawable->z_index = current_z_index;
|
||||
drawable->setParent(owner_ptr);
|
||||
self->data->push_back(drawable);
|
||||
};
|
||||
|
||||
// Add the item based on its type
|
||||
if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) {
|
||||
addDrawable(((PyUIFrameObject*)item)->data);
|
||||
}
|
||||
else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) {
|
||||
addDrawable(((PyUICaptionObject*)item)->data);
|
||||
}
|
||||
else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) {
|
||||
addDrawable(((PyUISpriteObject*)item)->data);
|
||||
}
|
||||
else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
addDrawable(((PyUIGridObject*)item)->data);
|
||||
}
|
||||
else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line"))) {
|
||||
addDrawable(((PyUILineObject*)item)->data);
|
||||
}
|
||||
else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle"))) {
|
||||
addDrawable(((PyUICircleObject*)item)->data);
|
||||
}
|
||||
else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc"))) {
|
||||
addDrawable(((PyUIArcObject*)item)->data);
|
||||
}
|
||||
else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Viewport3D"))) {
|
||||
addDrawable(((PyViewport3DObject*)item)->data);
|
||||
}
|
||||
drawable->removeFromParent();
|
||||
drawable->z_index = current_z_index;
|
||||
drawable->setParent(owner_ptr);
|
||||
self->data->push_back(drawable);
|
||||
|
||||
Py_DECREF(item);
|
||||
}
|
||||
|
|
@ -850,29 +688,11 @@ PyObject* UICollection::remove(PyUICollectionObject* self, PyObject* o)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// Type checking - must be a UIDrawable subclass
|
||||
if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Drawable"))) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"UICollection.remove requires a UI element (Frame, Caption, Sprite, Grid)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the C++ object from the Python object
|
||||
std::shared_ptr<UIDrawable> search_drawable = nullptr;
|
||||
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) {
|
||||
search_drawable = ((PyUIFrameObject*)o)->data;
|
||||
} else if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) {
|
||||
search_drawable = ((PyUICaptionObject*)o)->data;
|
||||
} else if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) {
|
||||
search_drawable = ((PyUISpriteObject*)o)->data;
|
||||
} else if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
search_drawable = ((PyUIGridObject*)o)->data;
|
||||
}
|
||||
|
||||
// Extract drawable from Python object
|
||||
std::shared_ptr<UIDrawable> search_drawable = extractDrawable(o);
|
||||
if (!search_drawable) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"UICollection.remove requires a UI element (Frame, Caption, Sprite, Grid)");
|
||||
"UICollection.remove requires a Drawable object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -951,29 +771,11 @@ PyObject* UICollection::insert(PyUICollectionObject* self, PyObject* args)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// Type checking - must be a UIDrawable subclass
|
||||
if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Drawable"))) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"UICollection.insert requires a UI element (Frame, Caption, Sprite, Grid)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the C++ object from the Python object
|
||||
std::shared_ptr<UIDrawable> drawable = nullptr;
|
||||
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) {
|
||||
drawable = ((PyUIFrameObject*)o)->data;
|
||||
} else if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) {
|
||||
drawable = ((PyUICaptionObject*)o)->data;
|
||||
} else if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) {
|
||||
drawable = ((PyUISpriteObject*)o)->data;
|
||||
} else if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
drawable = ((PyUIGridObject*)o)->data;
|
||||
}
|
||||
|
||||
// Extract drawable from Python object
|
||||
std::shared_ptr<UIDrawable> drawable = extractDrawable(o);
|
||||
if (!drawable) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"UICollection.insert requires a UI element (Frame, Caption, Sprite, Grid)");
|
||||
"UICollection.insert requires a Drawable object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -1011,30 +813,10 @@ PyObject* UICollection::index_method(PyUICollectionObject* self, PyObject* value
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// Type checking - must be a UIDrawable subclass
|
||||
if (!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) &&
|
||||
!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) &&
|
||||
!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) &&
|
||||
!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
PyErr_SetString(PyExc_TypeError, "UICollection.index requires a Frame, Caption, Sprite, or Grid object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the C++ object from the Python object
|
||||
std::shared_ptr<UIDrawable> search_drawable = nullptr;
|
||||
|
||||
if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) {
|
||||
search_drawable = ((PyUIFrameObject*)value)->data;
|
||||
} else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) {
|
||||
search_drawable = ((PyUICaptionObject*)value)->data;
|
||||
} else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) {
|
||||
search_drawable = ((PyUISpriteObject*)value)->data;
|
||||
} else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
search_drawable = ((PyUIGridObject*)value)->data;
|
||||
}
|
||||
|
||||
// Extract drawable from Python object
|
||||
std::shared_ptr<UIDrawable> search_drawable = extractDrawable(value);
|
||||
if (!search_drawable) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Failed to extract C++ object from Python object");
|
||||
PyErr_SetString(PyExc_TypeError, "UICollection.index requires a Drawable object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -1056,28 +838,8 @@ PyObject* UICollection::count(PyUICollectionObject* self, PyObject* value) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// Type checking - must be a UIDrawable subclass
|
||||
if (!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) &&
|
||||
!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) &&
|
||||
!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) &&
|
||||
!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
// Not a valid type, so count is 0
|
||||
return PyLong_FromLong(0);
|
||||
}
|
||||
|
||||
// Get the C++ object from the Python object
|
||||
std::shared_ptr<UIDrawable> search_drawable = nullptr;
|
||||
|
||||
if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) {
|
||||
search_drawable = ((PyUIFrameObject*)value)->data;
|
||||
} else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) {
|
||||
search_drawable = ((PyUICaptionObject*)value)->data;
|
||||
} else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) {
|
||||
search_drawable = ((PyUISpriteObject*)value)->data;
|
||||
} else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
search_drawable = ((PyUIGridObject*)value)->data;
|
||||
}
|
||||
|
||||
// Extract drawable (returns nullptr for non-drawable types)
|
||||
std::shared_ptr<UIDrawable> search_drawable = extractDrawable(value);
|
||||
if (!search_drawable) {
|
||||
return PyLong_FromLong(0);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue