Converted py_instance to a macro (don't ask me why it doesn't work as a function) and first pass at UICaption functionality. UISprite C++ tests.

This commit is contained in:
John McCardle 2023-09-03 20:40:52 -04:00
parent 5267287b05
commit 9486104377
5 changed files with 337 additions and 44 deletions

View File

@ -124,6 +124,7 @@ PyObject* PyInit_mcrfpy()
//std::cout << "Adding UIFrame object to module\n"; //std::cout << "Adding UIFrame object to module\n";
PyModule_AddType(m, &mcrfpydef::PyColorType); PyModule_AddType(m, &mcrfpydef::PyColorType);
PyModule_AddType(m, &mcrfpydef::PyUICaptionType);
if (PyModule_AddType(m, &mcrfpydef::PyUIFrameType) < 0) if (PyModule_AddType(m, &mcrfpydef::PyUIFrameType) < 0)
{ {
@ -135,6 +136,7 @@ PyObject* PyInit_mcrfpy()
PyModule_AddType(m, &mcrfpydef::PyUICollectionIterType); PyModule_AddType(m, &mcrfpydef::PyUICollectionIterType);
return m; return m;
} }

View File

@ -84,6 +84,16 @@ void UICaption::render(sf::Vector2f offset)
text.move(-offset); text.move(-offset);
} }
void UISprite::update()
{
auto& tex = Resources::game->textures[texture_index];
sprite.setTexture(tex.texture);
sprite.setScale(sf::Vector2f(scale, scale));
sprite.setPosition(sf::Vector2f(x, y));
//std::cout << "Drawable position: " << x << ", " << y << " -> " << s.getPosition().x << ", " << s.getPosition().y << std::endl;
sprite.setTextureRect(tex.spriteCoordinates(sprite_index));
}
void UISprite::render(sf::Vector2f offset) void UISprite::render(sf::Vector2f offset)
{ {
sprite.move(offset); sprite.move(offset);
@ -101,23 +111,10 @@ PyObjectsEnum UISprite::derived_type()
return PyObjectsEnum::UISPRITE; return PyObjectsEnum::UISPRITE;
} }
PyObject* mcrfpydef::py_instance(std::shared_ptr<UIDrawable> source) PyObject* DEFUNCT_py_instance(std::shared_ptr<UIDrawable> source)
{ {
// takes a UI drawable, calls its derived_type virtual function, and builds a Python object based on the return value. // takes a UI drawable, calls its derived_type virtual function, and builds a Python object based on the return value.
//using namespace mcrfpydef; using namespace mcrfpydef;
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<PyColorObject*>(pyColor);
pyColorObj->data = std::make_shared<sf::Color>();
pyColorObj->data-> r = 255;
return (PyObject*)pyColorObj;
PyObject* newobj = NULL; PyObject* newobj = NULL;
std::cout << "py_instance called\n"; std::cout << "py_instance called\n";

335
src/UI.h
View File

@ -81,9 +81,10 @@ public:
class UISprite: public UIDrawable class UISprite: public UIDrawable
{ {
public: public:
void update();
void render(sf::Vector2f) override final; void render(sf::Vector2f) override final;
int texture_index, sprite_index; int texture_index, sprite_index;
float scale; float x, y, scale;
sf::Sprite sprite; sf::Sprite sprite;
PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UISprite; }; PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UISprite; };
}; };
@ -115,7 +116,60 @@ typedef struct {
namespace mcrfpydef { namespace mcrfpydef {
PyObject* py_instance(std::shared_ptr<UIDrawable> source); //PyObject* py_instance(std::shared_ptr<UIDrawable> source);
// This function segfaults on tp_alloc for an unknown reason, but works inline with mcrfpydef:: methods.
#define RET_PY_INSTANCE(target) { \
switch (target->derived_type()) \
{ \
case PyObjectsEnum::UIFRAME: \
{ \
PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); \
if (o) \
{ \
auto p = std::static_pointer_cast<UIFrame>(target); \
o->data = p; \
auto utarget = o->data; \
} \
return (PyObject*)o; \
} \
case PyObjectsEnum::UICAPTION: \
{ \
PyUICaptionObject* o = (PyUICaptionObject*)((&PyUICaptionType)->tp_alloc(&PyUICaptionType, 0)); \
if (o) \
{ \
auto p = std::static_pointer_cast<UICaption>(target); \
o->data = p; \
auto utarget = o->data; \
} \
return (PyObject*)o; \
} \
case PyObjectsEnum::UISPRITE: \
{ \
PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); \
if (o) \
{ \
auto p = std::static_pointer_cast<UIFrame>(target); \
o->data = p; \
auto utarget = o->data; \
} \
return (PyObject*)o; \
} \
case PyObjectsEnum::UIGRID: \
{ \
PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); \
if (o) \
{ \
auto p = std::static_pointer_cast<UIFrame>(target); \
o->data = p; \
auto utarget = o->data; \
} \
return (PyObject*)o; \
} \
} \
}
// end macro definition
// Color Definitions // Color Definitions
// struct, members, new, set_member, PyTypeObject // struct, members, new, set_member, PyTypeObject
@ -225,6 +279,250 @@ namespace mcrfpydef {
} }
}; };
/*
*
* Begin template generation for PyUICaptionType
*
*/
static PyObject* PyUICaption_get_float_member(PyUICaptionObject* self, void* closure)
{
auto member_ptr = reinterpret_cast<long>(closure);
if (member_ptr == 0)
return PyFloat_FromDouble(self->data->text.getPosition().x);
else if (member_ptr == 1)
return PyFloat_FromDouble(self->data->text.getPosition().y);
else if (member_ptr == 4)
return PyFloat_FromDouble(self->data->text.getOutlineThickness());
else if (member_ptr == 5)
return PyLong_FromLong(self->data->text.getCharacterSize());
else
{
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
return nullptr;
}
}
static int PyUICaption_set_float_member(PyUICaptionObject* self, PyObject* value, void* closure)
{
float val;
auto member_ptr = reinterpret_cast<long>(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->text.setPosition(val, self->data->text.getPosition().y);
else if (member_ptr == 1) //y
self->data->text.setPosition(self->data->text.getPosition().x, val);
else if (member_ptr == 4) //outline
self->data->text.setOutlineThickness(val);
else if (member_ptr == 5) // character size
self->data->text.setCharacterSize(val);
return 0;
}
static PyObject* PyUICaption_get_color_member(PyUICaptionObject* self, void* closure)
{
// validate closure (should be impossible to be wrong, but it's thorough)
auto member_ptr = reinterpret_cast<long>(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<PyColorObject*>(pyColor);
// fetch correct member data
sf::Color color;
if (member_ptr == 0)
{
color = self->data->text.getFillColor();
//return Py_BuildValue("(iii)", color.r, color.g, color.b);
}
else if (member_ptr == 1)
{
color = self->data->text.getOutlineColor();
//return Py_BuildValue("(iii)", color.r, color.g, color.b);
}
// initialize new mcrfpy.Color instance
pyColorObj->data = std::make_shared<sf::Color>(color);
return pyColor;
}
static int PyUICaption_set_color_member(PyUICaptionObject* self, PyObject* value, void* closure)
{
auto member_ptr = reinterpret_cast<long>(closure);
int r, g, b, a;
if (PyObject_IsInstance(value, (PyObject*)&PyColorType))
{
// get value from mcrfpy.Color instance
PyColorObject* color = reinterpret_cast<PyColorObject*>(value);
r = color->data->r;
g = color->data->g;
b = color->data->b;
a = color->data->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->text.setFillColor(sf::Color(r, g, b, a));
}
else if (member_ptr == 1)
{
self->data->text.setOutlineColor(sf::Color(r, g, b, a));
}
else
{
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
return -1;
}
return 0;
}
static PyObject* PyUICaption_get_text(PyUICaptionObject* self, void* closure)
{
return PyUnicode_FromString("Test String, Please Ignore");
}
static int PyUICaption_set_text(PyUICaptionObject* self, PyObject* value, void* closure)
{
return 0;
}
static PyGetSetDef PyUICaption_getsetters[] = {
{"x", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "X coordinate of top-left corner", (void*)0},
{"y", (getter)PyUICaption_get_float_member, (setter)PyUICaption_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)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Thickness of the border", (void*)4},
{"fill_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Fill color of the text", (void*)0},
{"outline_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Outline color of the text", (void*)1},
//{"children", (getter)PyUIFrame_get_children, NULL, "UICollection of objects on top of this one", NULL},
{"text", (getter)PyUICaption_get_text, (setter)PyUICaption_set_text, "The text displayed", NULL},
{"size", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Text size (integer) in points", (void*)5},
{NULL}
};
static PyObject* PyUICaption_repr(PyUICaptionObject* self)
{
std::ostringstream ss;
if (!self->data) ss << "<Frame (invalid internal object)>";
else {
auto text = self->data->text;
auto fc = text.getFillColor();
auto oc = text.getOutlineColor();
ss << "<Caption (x=" << text.getPosition().x << ", y=" << text.getPosition().y << ", " <<
"text='" << (std::string)text.getString() << "', " <<
"outline=" << text.getOutlineThickness() << ", " <<
"fill_color=(" << (int)fc.r << ", " << (int)fc.g << ", " << (int)fc.b << ", " << (int)fc.a <<"), " <<
"outlinecolor=(" << (int)oc.r << ", " << (int)oc.g << ", " << (int)oc.b << ", " << (int)oc.a <<"), " <<
")>";
}
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
static int PyUICaption_init(PyUICaptionObject* self, PyObject* args, PyObject* kwds)
{
//std::cout << "Init called\n";
static const char* keywords[] = { "x", "y", "text", nullptr };
float x = 0.0f, y = 0.0f;
char* text;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffz", const_cast<char**>(keywords), &x, &y, &text))
{
return -1;
}
//self->data->x = x;
//self->data->y = y;
self->data->text.setFillColor(sf::Color(0,0,0,255));
self->data->text.setOutlineColor(sf::Color(128,128,128,255));
return 0;
}
static PyTypeObject PyUICaptionType = {
//PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mcrfpy.Caption",
.tp_basicsize = sizeof(PyUICaptionObject),
.tp_itemsize = 0,
.tp_dealloc = (destructor)[](PyObject* self)
{
PyUICaptionObject* obj = (PyUICaptionObject*)self;
obj->data.reset();
Py_TYPE(self)->tp_free(self);
},
.tp_repr = (reprfunc)PyUICaption_repr,
//.tp_hash = NULL,
//.tp_iter
//.tp_iternext
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("docstring"),
//.tp_methods = PyUIFrame_methods,
//.tp_members = PyUIFrame_members,
.tp_getset = PyUICaption_getsetters,
//.tp_base = NULL,
.tp_init = (initproc)PyUICaption_init,
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
{
PyUICaptionObject* self = (PyUICaptionObject*)type->tp_alloc(type, 0);
if (self) self->data = std::make_shared<UICaption>();
return (PyObject*)self;
}
};
/*
*
* End PyUICaptionType generation
*
*/
/* /*
* *
* Begin template generation for PyUIFrameType * Begin template generation for PyUIFrameType
@ -330,6 +628,7 @@ namespace mcrfpydef {
g = color->data->g; g = color->data->g;
b = color->data->b; b = color->data->b;
a = color->data->a; a = color->data->a;
std::cout << "using color: " << r << " " << g << " " << b << " " << a << std::endl;
} }
else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4)
{ {
@ -522,7 +821,8 @@ namespace mcrfpydef {
} }
auto target = (*vec)[self->index-1]; auto target = (*vec)[self->index-1];
// TODO build PyObject* of the correct UIDrawable subclass to return // TODO build PyObject* of the correct UIDrawable subclass to return
return py_instance(target); //return py_instance(target);
return NULL;
} }
static PyObject* PyUICollectionIter_repr(PyUICollectionIterObject* self) static PyObject* PyUICollectionIter_repr(PyUICollectionIterObject* self)
@ -582,11 +882,6 @@ namespace mcrfpydef {
} }
static PyObject* PyUICollection_getitem(PyUICollectionObject* self, Py_ssize_t index) { static PyObject* PyUICollection_getitem(PyUICollectionObject* self, Py_ssize_t index) {
if (index >= self->data->size())
{
// set exception text
return NULL;
}
// build a Python version of item at self->data[index] // build a Python version of item at self->data[index]
// Copy pasted:: // Copy pasted::
auto vec = self->data.get(); auto vec = self->data.get();
@ -602,27 +897,11 @@ namespace mcrfpydef {
return NULL; return NULL;
} }
auto target = (*vec)[index]; auto target = (*vec)[index];
// TODO build PyObject* of the correct UIDrawable subclass to return RET_PY_INSTANCE(target);
//return py_instance(target);
/*
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<PyColorObject*>(pyColor);
pyColorObj->data = std::make_shared<sf::Color>();
pyColorObj->data-> r = 255;
return (PyObject*)pyColorObj;
*/
// copy-pasted object determination & instantiation
/*
PyObject* newobj = NULL; PyObject* newobj = NULL;
std::cout << "Instantiating object\n"; std::cout << "Instantiating object\n";
switch (target->derived_type()) switch (target->derived_type())
@ -651,7 +930,7 @@ namespace mcrfpydef {
return (PyObject*)o; return (PyObject*)o;
} }
} }
*/

View File

@ -59,6 +59,20 @@ UITestScene::UITestScene(GameEngine* g) : Scene(g)
//ui_elements.push_back(&e1); //ui_elements.push_back(&e1);
//ui_elements.push_back(&e2); //ui_elements.push_back(&e2);
t.loadFromFile("./assets/kenney_tinydungeon.png");
t.setSmooth(false);
auto indextex = IndexTexture(t, 16, 12, 11);
Resources::game->textures.push_back(indextex);
//std::cout << Resources::game->textures.size() << " textures loaded.\n";
auto e3 = std::make_shared<UISprite>();
e3->x = 10; e3->y = 10;
e3->texture_index = 0;
e3->sprite_index = 84;
e3->scale = 4.0f;
e3->update();
e1aa->children->push_back(e3);
/* /*
// note - you can't use the pointer to UI elements in constructor. // note - you can't use the pointer to UI elements in constructor.
// The scene map is still being assigned to, so this object can't be looked up. // The scene map is still being assigned to, so this object can't be looked up.

View File

@ -12,6 +12,7 @@ class UITestScene: public Scene
//UIFrame e1, e1a, e1aa; //UIFrame e1, e1a, e1aa;
//UICaption e2; //UICaption e2;
//std::vector<UIDrawable*> ui_elements; //std::vector<UIDrawable*> ui_elements;
sf::Texture t;
public: public:
UITestScene(GameEngine*); UITestScene(GameEngine*);