diff --git a/src/UI.h b/src/UI.h index f81b3a6..7869cbb 100644 --- a/src/UI.h +++ b/src/UI.h @@ -184,6 +184,11 @@ typedef struct { PyObject* texture; } PyUISpriteObject; +typedef struct { + PyObject_HEAD + std::shared_ptr>> data; +} PyUICollectionObject; + typedef struct { PyObject_HEAD std::shared_ptr data; @@ -247,10 +252,10 @@ switch (target->derived_type()) \ } \ case PyObjectsEnum::UIGRID: \ { \ - PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); \ + PyUIGridObject* o = (PyUIGridObject*)((&PyUIGridType)->tp_alloc(&PyUIGridType, 0)); \ if (o) \ { \ - auto p = std::static_pointer_cast(target); \ + auto p = std::static_pointer_cast(target); \ o->data = p; \ auto utarget = o->data; \ } \ @@ -1186,326 +1191,8 @@ switch (target->derived_type()) \ * */ - /* - * - * Begin PyUICollectionIter defs - * - */ - typedef struct { - PyObject_HEAD - std::shared_ptr>> data; - int index; - int start_size; - } PyUICollectionIterObject; - - 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; - } - - 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; - } - - 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, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self; - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUICollectionIter_repr, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), - .tp_iternext = (iternextfunc)PyUICollectionIter_next, - //.tp_getset = PyUICollection_getset, - .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."); - return NULL; - } - }; - - /* - * - * End PyUICollectionIter defs - * - */ - /* - * - * Begin PyUICollection defs - * - */ - typedef struct { - PyObject_HEAD - std::shared_ptr>> data; - } PyUICollectionObject; - - static Py_ssize_t PyUICollection_len(PyUICollectionObject* self) { - return self->data->size(); - } - - 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); - - // copy-pasted object determination & instantiation - - /* - PyObject* newobj = NULL; - std::cout << "Instantiating object\n"; - switch (target->derived_type()) - { - case PyObjectsEnum::UIFRAME: - { - std::cout << "UIFRAME case\n" << std::flush; - PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); - if (o) - { - std::cout << "Casting data...\n" << std::flush; - auto p = std::static_pointer_cast(target); - std::cout << "casted. Assigning...\n" << std::flush; - //o->data = std::make_shared(); - - o->data = p; - //std::cout << "assigned.\n" << std::flush; - auto utarget = o->data; //(UIFrame*)target.get(); - std::cout << "Loaded data into object. " << utarget->box.getPosition().x << " " << utarget->box.getPosition().y << " " << - utarget->box.getSize().x << " " << utarget->box.getSize().y << std::endl; - } - else - { - std::cout << "Allocation failed.\n" << std::flush; - } - return (PyObject*)o; - } - } - */ - - - - - - - return NULL; - - - } - - 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) - }; - - 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); - } - - Py_INCREF(Py_None); - return Py_None; - } - - 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; - } - - 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} - }; - - 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"); - } - - 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; - } - - 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 PyGetSetDef PyUICollection_getsetters[] = { - {NULL} - }; - */ - - static PyTypeObject PyUICollectionType = { - //PyVarObject_/HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.UICollection", - .tp_basicsize = sizeof(PyUICollectionObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUICollectionObject* obj = (PyUICollectionObject*)self; - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .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)PyUICollection_iter, - .tp_methods = PyUICollection_methods, // append, remove - //.tp_getset = PyUICollection_getset, - .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? - // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return NULL; - } - }; - /* - * - * End PyUICollection defs - * - */ - - - static PyObject* PyUIFrame_get_children(PyUIFrameObject* self, void* closure) - { - // create PyUICollection instance pointing to self->data->children - PyUICollectionObject* o = (PyUICollectionObject*)PyUICollectionType.tp_alloc(&PyUICollectionType, 0); - if (o) - o->data = self->data->children; - return (PyObject*)o; - } /* * @@ -2190,7 +1877,287 @@ static void PyUIGrid_dealloc(PyUIGridObject* self) { + /* + * + * Begin PyUICollectionIter defs + * + */ + typedef struct { + PyObject_HEAD + std::shared_ptr>> data; + int index; + int start_size; + } PyUICollectionIterObject; + 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; + } + + 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; + } + + 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, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self; + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUICollectionIter_repr, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), + .tp_iternext = (iternextfunc)PyUICollectionIter_next, + //.tp_getset = PyUICollection_getset, + .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."); + return NULL; + } + }; + + /* + * + * End PyUICollectionIter defs + * + */ + + + /* + * + * Begin PyUICollection defs + * + */ + + static Py_ssize_t PyUICollection_len(PyUICollectionObject* self) { + return self->data->size(); + } + + 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; + + + } + + 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) + }; + + 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; + } + + 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; + } + + 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} + }; + + 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"); + } + + 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; + } + + 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 PyGetSetDef PyUICollection_getsetters[] = { + {NULL} + }; + */ + + static PyTypeObject PyUICollectionType = { + //PyVarObject_/HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.UICollection", + .tp_basicsize = sizeof(PyUICollectionObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUICollectionObject* obj = (PyUICollectionObject*)self; + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .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)PyUICollection_iter, + .tp_methods = PyUICollection_methods, // append, remove + //.tp_getset = PyUICollection_getset, + .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? + // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return NULL; + } + }; + /* + * + * End PyUICollection defs + * + */ + + static PyObject* PyUIFrame_get_children(PyUIFrameObject* self, void* closure) + { + // create PyUICollection instance pointing to self->data->children + PyUICollectionObject* o = (PyUICollectionObject*)PyUICollectionType.tp_alloc(&PyUICollectionType, 0); + if (o) + o->data = self->data->children; + return (PyObject*)o; + } } // namespace mcrfpydef