diff --git a/src/UICaption.cpp b/src/UICaption.cpp index 21c9c36..9d8c21d 100644 --- a/src/UICaption.cpp +++ b/src/UICaption.cpp @@ -115,7 +115,7 @@ int UICaption::set_color_member(PyUICaptionObject* self, PyObject* value, void* auto member_ptr = reinterpret_cast(closure); //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse int r, g, b, a; - if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color") /*(PyObject*)&mcrfpydef::PyColorType)*/)) + if (PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PyColorType)) { // get value from mcrfpy.Color instance auto c = ((PyColorObject*)value)->data; @@ -236,7 +236,7 @@ int UICaption::init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) // check types for font, fill_color, outline_color std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl; - if (font != NULL && !PyObject_IsInstance(font, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Font")/*(PyObject*)&PyFontType)*/)){ + if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){ PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); return -1; } else if (font != NULL) diff --git a/src/UICollection.cpp b/src/UICollection.cpp index 7aa16cb..8133952 100644 --- a/src/UICollection.cpp +++ b/src/UICollection.cpp @@ -1,203 +1,2 @@ #include "UICollection.h" -#include "UIFrame.h" -#include "UICaption.h" -#include "UISprite.h" -#include "UIGrid.h" -#include "McRFPy_API.h" - -using namespace mcrfpydef; - -int UICollectionIter::init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds) -{ - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return -1; -} - -PyObject* UICollectionIter::next(PyUICollectionIterObject* self) -{ - if (self->data->size() != self->start_size) - { - PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); - return NULL; - } - - if (self->index > self->start_size - 1) - { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } - self->index++; - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - auto target = (*vec)[self->index-1]; - // TODO build PyObject* of the correct UIDrawable subclass to return - //return py_instance(target); - return NULL; -} - -PyObject* UICollectionIter::repr(PyUICollectionIterObject* self) -{ - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects, @ index " << self->index << ")>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); -} - -Py_ssize_t UICollection::len(PyUICollectionObject* self) { - return self->data->size(); -} - -PyObject* UICollection::getitem(PyUICollectionObject* self, Py_ssize_t index) { - // build a Python version of item at self->data[index] - // Copy pasted:: - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - while (index < 0) index += self->data->size(); - if (index > self->data->size() - 1) - { - PyErr_SetString(PyExc_IndexError, "UICollection index out of range"); - return NULL; - } - auto target = (*vec)[index]; - RET_PY_INSTANCE(target); -return NULL; - - -} - -PySequenceMethods UICollection::sqmethods = { - .sq_length = (lenfunc)UICollection::len, - .sq_item = (ssizeargfunc)UICollection::getitem, - //.sq_item_by_index = PyUICollection_getitem - //.sq_slice - return a subset of the iterable - //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) - //.sq_ass_slice - cool; no thanks, for now - //.sq_contains - called when `x in o` is executed - //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) -}; - -/* 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 ); - - // 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")) - ) - { - PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection"); - return NULL; - } - - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) - { - PyUIFrameObject* frame = (PyUIFrameObject*)o; - self->data->push_back(frame->data); - } - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) - { - PyUICaptionObject* caption = (PyUICaptionObject*)o; - self->data->push_back(caption->data); - } - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) - { - PyUISpriteObject* sprite = (PyUISpriteObject*)o; - self->data->push_back(sprite->data); - } - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) - { - PyUIGridObject* grid = (PyUIGridObject*)o; - self->data->push_back(grid->data); - } - - Py_INCREF(Py_None); - return Py_None; -} - -PyObject* UICollection::remove(PyUICollectionObject* self, PyObject* o) -{ - if (!PyLong_Check(o)) - { - PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); - return NULL; - } - long index = PyLong_AsLong(o); - if (index >= self->data->size()) - { - PyErr_SetString(PyExc_ValueError, "Index out of range"); - return NULL; - } - else if (index < 0) - { - PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); - return NULL; - } - - // release the shared pointer at self->data[index]; - self->data->erase(self->data->begin() + index); - Py_INCREF(Py_None); - return Py_None; -} - -PyMethodDef UICollection::methods[] = { - {"append", (PyCFunction)UICollection::append, METH_O}, - //{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO - {"remove", (PyCFunction)UICollection::remove, METH_O}, - {NULL, NULL, 0, NULL} -}; - -PyObject* UICollection::repr(PyUICollectionObject* self) -{ - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects)>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); -} - -int UICollection::init(PyUICollectionObject* self, PyObject* args, PyObject* kwds) -{ - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return -1; -} - -PyObject* UICollection::iter(PyUICollectionObject* self) -{ - PyUICollectionIterObject* iterObj; - iterObj = (PyUICollectionIterObject*)PyUICollectionIterType.tp_alloc(&PyUICollectionIterType, 0); - if (iterObj == NULL) { - return NULL; // Failed to allocate memory for the iterator object - } - - iterObj->data = self->data; - iterObj->index = 0; - iterObj->start_size = self->data->size(); - - return (PyObject*)iterObj; -} diff --git a/src/UICollection.h b/src/UICollection.h index cf4b559..03136f0 100644 --- a/src/UICollection.h +++ b/src/UICollection.h @@ -1,53 +1,95 @@ #pragma once #include "Common.h" #include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" #include "UIDrawable.h" - -class UICollectionIter -{ - // really more of a namespace: all the members are public and static. But being consistent with other UI objects -public: - static int init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds); - static PyObject* next(PyUICollectionIterObject* self); - static PyObject* repr(PyUICollectionIterObject* self); -}; - -class UICollection -{ - // really more of a namespace: all the members are public and static. But being consistent with other UI objects -public: - static Py_ssize_t len(PyUICollectionObject* self); - static PyObject* getitem(PyUICollectionObject* self, Py_ssize_t index); - static PySequenceMethods sqmethods; - static PyObject* append(PyUICollectionObject* self, PyObject* o); - static PyObject* remove(PyUICollectionObject* self, PyObject* o); - static PyMethodDef methods[]; - static PyObject* repr(PyUICollectionObject* self); - static int init(PyUICollectionObject* self, PyObject* args, PyObject* kwds); - static PyObject* iter(PyUICollectionObject* self); -}; +#include "UICaption.h" +#include "UIFrame.h" +#include "UISprite.h" +#include "UIGrid.h" namespace mcrfpydef { + + typedef struct { + PyObject_HEAD + std::shared_ptr>> data; + int index; + int start_size; + } PyUICollectionIterObject; + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICollectionIter_init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds) + { + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return -1; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollectionIter_next(PyUICollectionIterObject* self) + { + if (self->data->size() != self->start_size) + { + PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); + return NULL; + } + + if (self->index > self->start_size - 1) + { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + self->index++; + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + auto target = (*vec)[self->index-1]; + // TODO build PyObject* of the correct UIDrawable subclass to return + //return py_instance(target); + return NULL; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollectionIter_repr(PyUICollectionIterObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects, @ index " << self->index << ")>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + static PyTypeObject PyUICollectionIterType = { //PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "mcrfpy.UICollectionIter", .tp_basicsize = sizeof(PyUICollectionIterObject), .tp_itemsize = 0, - //TODO - as static method, not inline lambda def, please .tp_dealloc = (destructor)[](PyObject* self) { PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self; obj->data.reset(); Py_TYPE(self)->tp_free(self); }, - .tp_repr = (reprfunc)UICollectionIter::repr, + .tp_repr = (reprfunc)PyUICollectionIter_repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), - .tp_iternext = (iternextfunc)UICollectionIter::next, + .tp_iternext = (iternextfunc)PyUICollectionIter_next, //.tp_getset = PyUICollection_getset, - .tp_init = (initproc)UICollectionIter::init, // just raise an exception - //TODO - as static method, not inline lambda def, please + .tp_init = (initproc)PyUICollectionIter_init, // just raise an exception .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* { PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); @@ -55,27 +97,176 @@ namespace mcrfpydef { } }; + //TODO: add this method to class scope; move implementation to .cpp file + static Py_ssize_t PyUICollection_len(PyUICollectionObject* self) { + return self->data->size(); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_getitem(PyUICollectionObject* self, Py_ssize_t index) { + // build a Python version of item at self->data[index] + // Copy pasted:: + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + while (index < 0) index += self->data->size(); + if (index > self->data->size() - 1) + { + PyErr_SetString(PyExc_IndexError, "UICollection index out of range"); + return NULL; + } + auto target = (*vec)[index]; + RET_PY_INSTANCE(target); + return NULL; + + + } + + //TODO: add this static array to class scope; move implementation to .cpp file + static PySequenceMethods PyUICollection_sqmethods = { + .sq_length = (lenfunc)PyUICollection_len, + .sq_item = (ssizeargfunc)PyUICollection_getitem, + //.sq_item_by_index = PyUICollection_getitem + //.sq_slice - return a subset of the iterable + //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) + //.sq_ass_slice - cool; no thanks, for now + //.sq_contains - called when `x in o` is executed + //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_append(PyUICollectionObject* self, PyObject* o) + { + // if not UIDrawable subclass, reject it + // self->data->push_back( c++ object inside o ); + + // this would be a great use case for .tp_base + if (!PyObject_IsInstance(o, (PyObject*)&PyUIFrameType) && + !PyObject_IsInstance(o, (PyObject*)&PyUISpriteType) && + !PyObject_IsInstance(o, (PyObject*)&PyUICaptionType) && + !PyObject_IsInstance(o, (PyObject*)&PyUIGridType) + ) + { + PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection"); + return NULL; + } + + if (PyObject_IsInstance(o, (PyObject*)&PyUIFrameType)) + { + PyUIFrameObject* frame = (PyUIFrameObject*)o; + self->data->push_back(frame->data); + } + if (PyObject_IsInstance(o, (PyObject*)&PyUICaptionType)) + { + PyUICaptionObject* caption = (PyUICaptionObject*)o; + self->data->push_back(caption->data); + } + if (PyObject_IsInstance(o, (PyObject*)&PyUISpriteType)) + { + PyUISpriteObject* sprite = (PyUISpriteObject*)o; + self->data->push_back(sprite->data); + } + if (PyObject_IsInstance(o, (PyObject*)&PyUIGridType)) + { + PyUIGridObject* grid = (PyUIGridObject*)o; + self->data->push_back(grid->data); + } + + Py_INCREF(Py_None); + return Py_None; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_remove(PyUICollectionObject* self, PyObject* o) + { + if (!PyLong_Check(o)) + { + PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); + return NULL; + } + long index = PyLong_AsLong(o); + if (index >= self->data->size()) + { + PyErr_SetString(PyExc_ValueError, "Index out of range"); + return NULL; + } + else if (index < 0) + { + PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); + return NULL; + } + + // release the shared pointer at self->data[index]; + self->data->erase(self->data->begin() + index); + Py_INCREF(Py_None); + return Py_None; + } + + //TODO: add this static array to class scope; move implementation to .cpp file + static PyMethodDef PyUICollection_methods[] = { + {"append", (PyCFunction)PyUICollection_append, METH_O}, + //{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO + {"remove", (PyCFunction)PyUICollection_remove, METH_O}, + {NULL, NULL, 0, NULL} + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_repr(PyUICollectionObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects)>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICollection_init(PyUICollectionObject* self, PyObject* args, PyObject* kwds) + { + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return -1; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_iter(PyUICollectionObject* self) + { + PyUICollectionIterObject* iterObj; + iterObj = (PyUICollectionIterObject*)PyUICollectionIterType.tp_alloc(&PyUICollectionIterType, 0); + if (iterObj == NULL) { + return NULL; // Failed to allocate memory for the iterator object + } + + iterObj->data = self->data; + iterObj->index = 0; + iterObj->start_size = self->data->size(); + + return (PyObject*)iterObj; + } + static PyTypeObject PyUICollectionType = { //PyVarObject_/HEAD_INIT(NULL, 0) .tp_name = "mcrfpy.UICollection", .tp_basicsize = sizeof(PyUICollectionObject), .tp_itemsize = 0, - //TODO - as static method, not inline lambda def, please .tp_dealloc = (destructor)[](PyObject* self) { PyUICollectionObject* obj = (PyUICollectionObject*)self; obj->data.reset(); Py_TYPE(self)->tp_free(self); }, - .tp_repr = (reprfunc)UICollection::repr, - .tp_as_sequence = &UICollection::sqmethods, + .tp_repr = (reprfunc)PyUICollection_repr, + .tp_as_sequence = &PyUICollection_sqmethods, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("Iterable, indexable collection of UI objects"), - .tp_iter = (getiterfunc)UICollection::iter, - .tp_methods = UICollection::methods, // append, remove + .tp_iter = (getiterfunc)PyUICollection_iter, + .tp_methods = PyUICollection_methods, // append, remove //.tp_getset = PyUICollection_getset, - .tp_init = (initproc)UICollection::init, // just raise an exception - //TODO - as static method, not inline lambda def, please + .tp_init = (initproc)PyUICollection_init, // just raise an exception .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* { // Does PyUICollectionType need __new__ if it's not supposed to be instantiable by the user? diff --git a/src/UIDrawable.h b/src/UIDrawable.h index 4e636c5..d1a6b6e 100644 --- a/src/UIDrawable.h +++ b/src/UIDrawable.h @@ -48,13 +48,6 @@ typedef struct { std::shared_ptr>> data; } PyUICollectionObject; -typedef struct { - PyObject_HEAD - std::shared_ptr>> data; - int index; - int start_size; -} PyUICollectionIterObject; - namespace mcrfpydef { //PyObject* py_instance(std::shared_ptr source); // This function segfaults on tp_alloc for an unknown reason, but works inline with mcrfpydef:: methods.