From 5d6af324bf2a6772a681a62b9dbe399b9c4c78d8 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Thu, 18 Apr 2024 22:14:57 -0400 Subject: [PATCH] UIFrame - moving static method into class namespace; no type object access --- src/UIFrame.cpp | 205 ++++++++++++++++++++++++++++++++++++++++++--- src/UIFrame.h | 218 +++--------------------------------------------- 2 files changed, 206 insertions(+), 217 deletions(-) diff --git a/src/UIFrame.cpp b/src/UIFrame.cpp index 3452a58..8c9561c 100644 --- a/src/UIFrame.cpp +++ b/src/UIFrame.cpp @@ -58,23 +58,208 @@ void UIFrame::render(sf::Vector2f offset) PyObject* UIFrame::get_children(PyUIFrameObject* self, void* closure) { // create PyUICollection instance pointing to self->data->children - PyUICollectionObject* o = (PyUICollectionObject*)mcrfpydef::PyUICollectionType.tp_alloc(&mcrfpydef::PyUICollectionType, 0); + //PyUICollectionObject* o = (PyUICollectionObject*)mcrfpydef::PyUICollectionType.tp_alloc(&mcrfpydef::PyUICollectionType, 0); + auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "UICollection"); + auto o = (PyUICollectionObject*)type->tp_alloc(type, 0); if (o) o->data = self->data->children; return (PyObject*)o; } -namespace mcrfpydef { - - // TODO: move to class scope; but this should at least get us compiling - static PyObject* PyUIFrame_get_children(PyUIFrameObject* self, void* closure) +PyObject* UIFrame::get_float_member(PyUIFrameObject* self, void* closure) +{ + auto member_ptr = reinterpret_cast(closure); + if (member_ptr == 0) + return PyFloat_FromDouble(self->data->box.getPosition().x); + else if (member_ptr == 1) + return PyFloat_FromDouble(self->data->box.getPosition().y); + else if (member_ptr == 2) + return PyFloat_FromDouble(self->data->box.getSize().x); + else if (member_ptr == 3) + return PyFloat_FromDouble(self->data->box.getSize().y); + else if (member_ptr == 4) + return PyFloat_FromDouble(self->data->box.getOutlineThickness()); + else { - // 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; + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; } } +int UIFrame::set_float_member(PyUIFrameObject* self, PyObject* value, void* closure) +{ + float val; + auto member_ptr = reinterpret_cast(closure); + if (PyFloat_Check(value)) + { + val = PyFloat_AsDouble(value); + } + else if (PyLong_Check(value)) + { + val = PyLong_AsLong(value); + } + else + { + PyErr_SetString(PyExc_TypeError, "Value must be an integer."); + return -1; + } + if (member_ptr == 0) //x + self->data->box.setPosition(val, self->data->box.getPosition().y); + else if (member_ptr == 1) //y + self->data->box.setPosition(self->data->box.getPosition().x, val); + else if (member_ptr == 2) //w + self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); + else if (member_ptr == 3) //h + self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); + else if (member_ptr == 4) //outline + self->data->box.setOutlineThickness(val); + return 0; +} + +PyObject* UIFrame::get_color_member(PyUIFrameObject* self, void* closure) +{ + // validate closure (should be impossible to be wrong, but it's thorough) + auto member_ptr = reinterpret_cast(closure); + if (member_ptr != 0 && member_ptr != 1) + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + //PyTypeObject* colorType = &PyColorType; + auto colorType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"); + PyObject* pyColor = colorType->tp_alloc(colorType, 0); + if (pyColor == NULL) + { + std::cout << "failure to allocate mcrfpy.Color / PyColorType" << std::endl; + return NULL; + } + PyColorObject* pyColorObj = reinterpret_cast(pyColor); + + // fetch correct member data + sf::Color color; + if (member_ptr == 0) + { + color = self->data->box.getFillColor(); + //return Py_BuildValue("(iii)", color.r, color.g, color.b); + } + else if (member_ptr == 1) + { + color = self->data->box.getOutlineColor(); + //return Py_BuildValue("(iii)", color.r, color.g, color.b); + } + + return PyColor(color).pyObject(); +} + +int UIFrame::set_color_member(PyUIFrameObject* self, PyObject* value, void* closure) +{ + //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse + auto member_ptr = reinterpret_cast(closure); + int r, g, b, a; + if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"))) + { + sf::Color c = ((PyColorObject*)value)->data; + r = c.r; g = c.g; b = c.b; a = c.a; + } + else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) + { + // reject non-Color, non-tuple value + PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); + return -1; + } + else // get value from tuples + { + r = PyLong_AsLong(PyTuple_GetItem(value, 0)); + g = PyLong_AsLong(PyTuple_GetItem(value, 1)); + b = PyLong_AsLong(PyTuple_GetItem(value, 2)); + a = 255; + + if (PyTuple_Size(value) == 4) + { + a = PyLong_AsLong(PyTuple_GetItem(value, 3)); + } + } + + if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) + { + PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); + return -1; + } + + if (member_ptr == 0) + { + self->data->box.setFillColor(sf::Color(r, g, b, a)); + } + else if (member_ptr == 1) + { + self->data->box.setOutlineColor(sf::Color(r, g, b, a)); + } + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return -1; + } + + return 0; +} + +PyGetSetDef UIFrame::getsetters[] = { + {"x", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "X coordinate of top-left corner", (void*)0}, + {"y", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "Y coordinate of top-left corner", (void*)1}, + {"w", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "width of the rectangle", (void*)2}, + {"h", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "height of the rectangle", (void*)3}, + {"outline", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "Thickness of the border", (void*)4}, + {"fill_color", (getter)UIFrame::get_color_member, (setter)UIFrame::set_color_member, "Fill color of the rectangle", (void*)0}, + {"outline_color", (getter)UIFrame::get_color_member, (setter)UIFrame::set_color_member, "Outline color of the rectangle", (void*)1}, + {"children", (getter)UIFrame::get_children, NULL, "UICollection of objects on top of this one", NULL}, + {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIFRAME}, + {NULL} +}; + +PyObject* UIFrame::repr(PyUIFrameObject* self) +{ + std::ostringstream ss; + if (!self->data) ss << ""; + else { + auto box = self->data->box; + auto fc = box.getFillColor(); + auto oc = box.getOutlineColor(); + ss << "data->children->size() << " child objects" << + ")>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +} + +int UIFrame::init(PyUIFrameObject* self, PyObject* args, PyObject* kwds) +{ + //std::cout << "Init called\n"; + const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr }; + float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f; + PyObject* fill_color = 0; + PyObject* outline_color = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffff|OOf", const_cast(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline)) + { + return -1; + } + + self->data->box.setPosition(sf::Vector2f(x, y)); + self->data->box.setSize(sf::Vector2f(w, h)); + self->data->box.setOutlineThickness(outline); + // getsetter abuse because I haven't standardized Color object parsing (TODO) + int err_val = 0; + if (fill_color && fill_color != Py_None) err_val = UIFrame::set_color_member(self, fill_color, (void*)0); + else self->data->box.setFillColor(sf::Color(0,0,0,255)); + if (err_val) return err_val; + if (outline_color && outline_color != Py_None) err_val = UIFrame::set_color_member(self, outline_color, (void*)1); + else self->data->box.setOutlineColor(sf::Color(128,128,128,255)); + if (err_val) return err_val; + return 0; +} diff --git a/src/UIFrame.h b/src/UIFrame.h index 356f0cb..e937ac0 100644 --- a/src/UIFrame.h +++ b/src/UIFrame.h @@ -34,215 +34,19 @@ public: virtual UIDrawable* click_at(sf::Vector2f point) override final; static PyObject* get_children(PyUIFrameObject* self, void* closure); + + static PyObject* get_float_member(PyUIFrameObject* self, void* closure); + static int set_float_member(PyUIFrameObject* self, PyObject* value, void* closure); + static PyObject* get_color_member(PyUIFrameObject* self, void* closure); + static int set_color_member(PyUIFrameObject* self, PyObject* value, void* closure); + static PyGetSetDef getsetters[]; + static PyObject* repr(PyUIFrameObject* self); + static int init(PyUIFrameObject* self, PyObject* args, PyObject* kwds); }; namespace mcrfpydef { //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUIFrame_get_float_member(PyUIFrameObject* self, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - if (member_ptr == 0) - return PyFloat_FromDouble(self->data->box.getPosition().x); - else if (member_ptr == 1) - return PyFloat_FromDouble(self->data->box.getPosition().y); - else if (member_ptr == 2) - return PyFloat_FromDouble(self->data->box.getSize().x); - else if (member_ptr == 3) - return PyFloat_FromDouble(self->data->box.getSize().y); - else if (member_ptr == 4) - return PyFloat_FromDouble(self->data->box.getOutlineThickness()); - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - } - //TODO: add this method to class scope; move implementation to .cpp file - static int PyUIFrame_set_float_member(PyUIFrameObject* self, PyObject* value, void* closure) - { - float val; - auto member_ptr = reinterpret_cast(closure); - if (PyFloat_Check(value)) - { - val = PyFloat_AsDouble(value); - } - else if (PyLong_Check(value)) - { - val = PyLong_AsLong(value); - } - else - { - PyErr_SetString(PyExc_TypeError, "Value must be an integer."); - return -1; - } - if (member_ptr == 0) //x - self->data->box.setPosition(val, self->data->box.getPosition().y); - else if (member_ptr == 1) //y - self->data->box.setPosition(self->data->box.getPosition().x, val); - else if (member_ptr == 2) //w - self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); - else if (member_ptr == 3) //h - self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); - else if (member_ptr == 4) //outline - self->data->box.setOutlineThickness(val); - return 0; - } - - //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUIFrame_get_color_member(PyUIFrameObject* self, void* closure) - { - // validate closure (should be impossible to be wrong, but it's thorough) - auto member_ptr = reinterpret_cast(closure); - if (member_ptr != 0 && member_ptr != 1) - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - PyTypeObject* colorType = &PyColorType; - PyObject* pyColor = colorType->tp_alloc(colorType, 0); - if (pyColor == NULL) - { - std::cout << "failure to allocate mcrfpy.Color / PyColorType" << std::endl; - return NULL; - } - PyColorObject* pyColorObj = reinterpret_cast(pyColor); - - // fetch correct member data - sf::Color color; - if (member_ptr == 0) - { - color = self->data->box.getFillColor(); - //return Py_BuildValue("(iii)", color.r, color.g, color.b); - } - else if (member_ptr == 1) - { - color = self->data->box.getOutlineColor(); - //return Py_BuildValue("(iii)", color.r, color.g, color.b); - } - - return PyColor(color).pyObject(); - } - - //TODO: add this method to class scope; move implementation to .cpp file - static int PyUIFrame_set_color_member(PyUIFrameObject* self, PyObject* value, void* closure) - { - //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse - auto member_ptr = reinterpret_cast(closure); - int r, g, b, a; - if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) - { - sf::Color c = ((PyColorObject*)value)->data; - r = c.r; g = c.g; b = c.b; a = c.a; - } - else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) - { - // reject non-Color, non-tuple value - PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); - return -1; - } - else // get value from tuples - { - r = PyLong_AsLong(PyTuple_GetItem(value, 0)); - g = PyLong_AsLong(PyTuple_GetItem(value, 1)); - b = PyLong_AsLong(PyTuple_GetItem(value, 2)); - a = 255; - - if (PyTuple_Size(value) == 4) - { - a = PyLong_AsLong(PyTuple_GetItem(value, 3)); - } - } - - if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) - { - PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); - return -1; - } - - if (member_ptr == 0) - { - self->data->box.setFillColor(sf::Color(r, g, b, a)); - } - else if (member_ptr == 1) - { - self->data->box.setOutlineColor(sf::Color(r, g, b, a)); - } - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return -1; - } - - return 0; - } - - //TODO: add this method to class scope; move implementation to .cpp file - //static PyObject* PyUIFrame_get_children(PyUIFrameObject*, void*); // forward declaration until UICollection is defined - // implementation after the PyUICollectionType definition - - //TODO: add this static array to class scope; move implementation to .cpp file - static PyGetSetDef PyUIFrame_getsetters[] = { - {"x", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "X coordinate of top-left corner", (void*)0}, - {"y", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Y coordinate of top-left corner", (void*)1}, - {"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, - {"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, - {"outline", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Thickness of the border", (void*)4}, - {"fill_color", (getter)PyUIFrame_get_color_member, (setter)PyUIFrame_set_color_member, "Fill color of the rectangle", (void*)0}, - {"outline_color", (getter)PyUIFrame_get_color_member, (setter)PyUIFrame_set_color_member, "Outline color of the rectangle", (void*)1}, - {"children", (getter)UIFrame::get_children, NULL, "UICollection of objects on top of this one", NULL}, - {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIFRAME}, - {NULL} - }; - - //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUIFrame_repr(PyUIFrameObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - auto box = self->data->box; - auto fc = box.getFillColor(); - auto oc = box.getOutlineColor(); - ss << "data->children->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 PyUIFrame_init(PyUIFrameObject* self, PyObject* args, PyObject* kwds) - { - //std::cout << "Init called\n"; - static const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr }; - float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f; - PyObject* fill_color = 0; - PyObject* outline_color = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffff|OOf", const_cast(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline)) - { - return -1; - } - - self->data->box.setPosition(sf::Vector2f(x, y)); - self->data->box.setSize(sf::Vector2f(w, h)); - self->data->box.setOutlineThickness(outline); - // getsetter abuse because I haven't standardized Color object parsing (TODO) - int err_val = 0; - if (fill_color && fill_color != Py_None) err_val = PyUIFrame_set_color_member(self, fill_color, (void*)0); - else self->data->box.setFillColor(sf::Color(0,0,0,255)); - if (err_val) return err_val; - if (outline_color && outline_color != Py_None) err_val = PyUIFrame_set_color_member(self, outline_color, (void*)1); - else self->data->box.setOutlineColor(sf::Color(128,128,128,255)); - if (err_val) return err_val; - return 0; - } static PyTypeObject PyUIFrameType = { //PyVarObject_HEAD_INIT(NULL, 0) @@ -255,7 +59,7 @@ namespace mcrfpydef { obj->data.reset(); Py_TYPE(self)->tp_free(self); }, - .tp_repr = (reprfunc)PyUIFrame_repr, + .tp_repr = (reprfunc)UIFrame::repr, //.tp_hash = NULL, //.tp_iter //.tp_iternext @@ -263,9 +67,9 @@ namespace mcrfpydef { .tp_doc = PyDoc_STR("docstring"), //.tp_methods = PyUIFrame_methods, //.tp_members = PyUIFrame_members, - .tp_getset = PyUIFrame_getsetters, + .tp_getset = UIFrame::getsetters, //.tp_base = NULL, - .tp_init = (initproc)PyUIFrame_init, + .tp_init = (initproc)UIFrame::init, .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* { PyUIFrameObject* self = (PyUIFrameObject*)type->tp_alloc(type, 0);