McRogueFace/src/UIDrawable.cpp

482 lines
16 KiB
C++

#include "UIDrawable.h"
#include "UIFrame.h"
#include "UICaption.h"
#include "UISprite.h"
#include "UIGrid.h"
#include "GameEngine.h"
#include "McRFPy_API.h"
UIDrawable::UIDrawable() : position(0.0f, 0.0f) { click_callable = NULL; }
void UIDrawable::click_unregister()
{
click_callable.reset();
}
void UIDrawable::render()
{
render(sf::Vector2f(), Resources::game->getRenderTarget());
}
PyObject* UIDrawable::get_click(PyObject* self, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure)); // trust me bro, it's an Enum
PyObject* ptr;
switch (objtype)
{
case PyObjectsEnum::UIFRAME:
if (((PyUIFrameObject*)self)->data->click_callable)
ptr = ((PyUIFrameObject*)self)->data->click_callable->borrow();
else
ptr = NULL;
break;
case PyObjectsEnum::UICAPTION:
if (((PyUICaptionObject*)self)->data->click_callable)
ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow();
else
ptr = NULL;
break;
case PyObjectsEnum::UISPRITE:
if (((PyUISpriteObject*)self)->data->click_callable)
ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow();
else
ptr = NULL;
break;
case PyObjectsEnum::UIGRID:
if (((PyUIGridObject*)self)->data->click_callable)
ptr = ((PyUIGridObject*)self)->data->click_callable->borrow();
else
ptr = NULL;
break;
default:
PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _get_click");
return NULL;
}
if (ptr && ptr != Py_None)
return ptr;
else
return Py_None;
}
int UIDrawable::set_click(PyObject* self, PyObject* value, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure)); // trust me bro, it's an Enum
UIDrawable* target;
switch (objtype)
{
case PyObjectsEnum::UIFRAME:
target = (((PyUIFrameObject*)self)->data.get());
break;
case PyObjectsEnum::UICAPTION:
target = (((PyUICaptionObject*)self)->data.get());
break;
case PyObjectsEnum::UISPRITE:
target = (((PyUISpriteObject*)self)->data.get());
break;
case PyObjectsEnum::UIGRID:
target = (((PyUIGridObject*)self)->data.get());
break;
default:
PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _set_click");
return -1;
}
if (value == Py_None)
{
target->click_unregister();
} else {
target->click_register(value);
}
return 0;
}
void UIDrawable::click_register(PyObject* callable)
{
click_callable = std::make_unique<PyClickCallable>(callable);
}
PyObject* UIDrawable::get_int(PyObject* self, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure));
UIDrawable* drawable = nullptr;
switch (objtype) {
case PyObjectsEnum::UIFRAME:
drawable = ((PyUIFrameObject*)self)->data.get();
break;
case PyObjectsEnum::UICAPTION:
drawable = ((PyUICaptionObject*)self)->data.get();
break;
case PyObjectsEnum::UISPRITE:
drawable = ((PyUISpriteObject*)self)->data.get();
break;
case PyObjectsEnum::UIGRID:
drawable = ((PyUIGridObject*)self)->data.get();
break;
default:
PyErr_SetString(PyExc_TypeError, "Invalid UIDrawable derived instance");
return NULL;
}
return PyLong_FromLong(drawable->z_index);
}
int UIDrawable::set_int(PyObject* self, PyObject* value, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure));
UIDrawable* drawable = nullptr;
switch (objtype) {
case PyObjectsEnum::UIFRAME:
drawable = ((PyUIFrameObject*)self)->data.get();
break;
case PyObjectsEnum::UICAPTION:
drawable = ((PyUICaptionObject*)self)->data.get();
break;
case PyObjectsEnum::UISPRITE:
drawable = ((PyUISpriteObject*)self)->data.get();
break;
case PyObjectsEnum::UIGRID:
drawable = ((PyUIGridObject*)self)->data.get();
break;
default:
PyErr_SetString(PyExc_TypeError, "Invalid UIDrawable derived instance");
return -1;
}
if (!PyLong_Check(value)) {
PyErr_SetString(PyExc_TypeError, "z_index must be an integer");
return -1;
}
long z = PyLong_AsLong(value);
if (z == -1 && PyErr_Occurred()) {
return -1;
}
// Clamp to int range
if (z < INT_MIN) z = INT_MIN;
if (z > INT_MAX) z = INT_MAX;
int old_z_index = drawable->z_index;
drawable->z_index = static_cast<int>(z);
// Notify of z_index change
if (old_z_index != drawable->z_index) {
drawable->notifyZIndexChanged();
}
return 0;
}
void UIDrawable::notifyZIndexChanged() {
// Mark the current scene as needing sort
// This works for elements in the scene's ui_elements collection
McRFPy_API::markSceneNeedsSort();
// TODO: In the future, we could add parent tracking to handle Frame children
// For now, Frame children will need manual sorting or collection modification
// to trigger a resort
}
PyObject* UIDrawable::get_name(PyObject* self, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure));
UIDrawable* drawable = nullptr;
switch (objtype) {
case PyObjectsEnum::UIFRAME:
drawable = ((PyUIFrameObject*)self)->data.get();
break;
case PyObjectsEnum::UICAPTION:
drawable = ((PyUICaptionObject*)self)->data.get();
break;
case PyObjectsEnum::UISPRITE:
drawable = ((PyUISpriteObject*)self)->data.get();
break;
case PyObjectsEnum::UIGRID:
drawable = ((PyUIGridObject*)self)->data.get();
break;
default:
PyErr_SetString(PyExc_TypeError, "Invalid UIDrawable derived instance");
return NULL;
}
return PyUnicode_FromString(drawable->name.c_str());
}
int UIDrawable::set_name(PyObject* self, PyObject* value, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure));
UIDrawable* drawable = nullptr;
switch (objtype) {
case PyObjectsEnum::UIFRAME:
drawable = ((PyUIFrameObject*)self)->data.get();
break;
case PyObjectsEnum::UICAPTION:
drawable = ((PyUICaptionObject*)self)->data.get();
break;
case PyObjectsEnum::UISPRITE:
drawable = ((PyUISpriteObject*)self)->data.get();
break;
case PyObjectsEnum::UIGRID:
drawable = ((PyUIGridObject*)self)->data.get();
break;
default:
PyErr_SetString(PyExc_TypeError, "Invalid UIDrawable derived instance");
return -1;
}
if (value == NULL || value == Py_None) {
drawable->name = "";
return 0;
}
if (!PyUnicode_Check(value)) {
PyErr_SetString(PyExc_TypeError, "name must be a string");
return -1;
}
const char* name_str = PyUnicode_AsUTF8(value);
if (!name_str) {
return -1;
}
drawable->name = name_str;
return 0;
}
void UIDrawable::enableRenderTexture(unsigned int width, unsigned int height) {
// Create or recreate RenderTexture if size changed
if (!render_texture || render_texture->getSize().x != width || render_texture->getSize().y != height) {
render_texture = std::make_unique<sf::RenderTexture>();
if (!render_texture->create(width, height)) {
render_texture.reset();
use_render_texture = false;
return;
}
render_sprite.setTexture(render_texture->getTexture());
}
use_render_texture = true;
render_dirty = true;
}
void UIDrawable::updateRenderTexture() {
if (!use_render_texture || !render_texture) {
return;
}
// Clear the RenderTexture
render_texture->clear(sf::Color::Transparent);
// Render content to RenderTexture
// This will be overridden by derived classes
// For now, just display the texture
render_texture->display();
// Update the sprite
render_sprite.setTexture(render_texture->getTexture());
}
PyObject* UIDrawable::get_float_member(PyObject* self, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<intptr_t>(closure) >> 8);
int member = reinterpret_cast<intptr_t>(closure) & 0xFF;
UIDrawable* drawable = nullptr;
switch (objtype) {
case PyObjectsEnum::UIFRAME:
drawable = ((PyUIFrameObject*)self)->data.get();
break;
case PyObjectsEnum::UICAPTION:
drawable = ((PyUICaptionObject*)self)->data.get();
break;
case PyObjectsEnum::UISPRITE:
drawable = ((PyUISpriteObject*)self)->data.get();
break;
case PyObjectsEnum::UIGRID:
drawable = ((PyUIGridObject*)self)->data.get();
break;
default:
PyErr_SetString(PyExc_TypeError, "Invalid UIDrawable derived instance");
return NULL;
}
switch (member) {
case 0: // x
return PyFloat_FromDouble(drawable->position.x);
case 1: // y
return PyFloat_FromDouble(drawable->position.y);
case 2: // w (width) - delegate to get_bounds
return PyFloat_FromDouble(drawable->get_bounds().width);
case 3: // h (height) - delegate to get_bounds
return PyFloat_FromDouble(drawable->get_bounds().height);
default:
PyErr_SetString(PyExc_AttributeError, "Invalid float member");
return NULL;
}
}
int UIDrawable::set_float_member(PyObject* self, PyObject* value, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<intptr_t>(closure) >> 8);
int member = reinterpret_cast<intptr_t>(closure) & 0xFF;
UIDrawable* drawable = nullptr;
switch (objtype) {
case PyObjectsEnum::UIFRAME:
drawable = ((PyUIFrameObject*)self)->data.get();
break;
case PyObjectsEnum::UICAPTION:
drawable = ((PyUICaptionObject*)self)->data.get();
break;
case PyObjectsEnum::UISPRITE:
drawable = ((PyUISpriteObject*)self)->data.get();
break;
case PyObjectsEnum::UIGRID:
drawable = ((PyUIGridObject*)self)->data.get();
break;
default:
PyErr_SetString(PyExc_TypeError, "Invalid UIDrawable derived instance");
return -1;
}
float val = 0.0f;
if (PyFloat_Check(value)) {
val = PyFloat_AsDouble(value);
} else if (PyLong_Check(value)) {
val = static_cast<float>(PyLong_AsLong(value));
} else {
PyErr_SetString(PyExc_TypeError, "Value must be a number (int or float)");
return -1;
}
switch (member) {
case 0: // x
drawable->position.x = val;
drawable->onPositionChanged();
break;
case 1: // y
drawable->position.y = val;
drawable->onPositionChanged();
break;
case 2: // w
case 3: // h
{
sf::FloatRect bounds = drawable->get_bounds();
if (member == 2) {
drawable->resize(val, bounds.height);
} else {
drawable->resize(bounds.width, val);
}
}
break;
default:
PyErr_SetString(PyExc_AttributeError, "Invalid float member");
return -1;
}
return 0;
}
PyObject* UIDrawable::get_pos(PyObject* self, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure));
UIDrawable* drawable = nullptr;
switch (objtype) {
case PyObjectsEnum::UIFRAME:
drawable = ((PyUIFrameObject*)self)->data.get();
break;
case PyObjectsEnum::UICAPTION:
drawable = ((PyUICaptionObject*)self)->data.get();
break;
case PyObjectsEnum::UISPRITE:
drawable = ((PyUISpriteObject*)self)->data.get();
break;
case PyObjectsEnum::UIGRID:
drawable = ((PyUIGridObject*)self)->data.get();
break;
default:
PyErr_SetString(PyExc_TypeError, "Invalid UIDrawable derived instance");
return NULL;
}
// Create a Python Vector object from position
PyObject* module = PyImport_ImportModule("mcrfpy");
if (!module) return NULL;
PyObject* vector_type = PyObject_GetAttrString(module, "Vector");
Py_DECREF(module);
if (!vector_type) return NULL;
PyObject* args = Py_BuildValue("(ff)", drawable->position.x, drawable->position.y);
PyObject* result = PyObject_CallObject(vector_type, args);
Py_DECREF(vector_type);
Py_DECREF(args);
return result;
}
int UIDrawable::set_pos(PyObject* self, PyObject* value, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure));
UIDrawable* drawable = nullptr;
switch (objtype) {
case PyObjectsEnum::UIFRAME:
drawable = ((PyUIFrameObject*)self)->data.get();
break;
case PyObjectsEnum::UICAPTION:
drawable = ((PyUICaptionObject*)self)->data.get();
break;
case PyObjectsEnum::UISPRITE:
drawable = ((PyUISpriteObject*)self)->data.get();
break;
case PyObjectsEnum::UIGRID:
drawable = ((PyUIGridObject*)self)->data.get();
break;
default:
PyErr_SetString(PyExc_TypeError, "Invalid UIDrawable derived instance");
return -1;
}
// Accept tuple or Vector
float x, y;
if (PyTuple_Check(value) && PyTuple_Size(value) == 2) {
PyObject* x_obj = PyTuple_GetItem(value, 0);
PyObject* y_obj = PyTuple_GetItem(value, 1);
if (PyFloat_Check(x_obj) || PyLong_Check(x_obj)) {
x = PyFloat_Check(x_obj) ? PyFloat_AsDouble(x_obj) : static_cast<float>(PyLong_AsLong(x_obj));
} else {
PyErr_SetString(PyExc_TypeError, "Position x must be a number");
return -1;
}
if (PyFloat_Check(y_obj) || PyLong_Check(y_obj)) {
y = PyFloat_Check(y_obj) ? PyFloat_AsDouble(y_obj) : static_cast<float>(PyLong_AsLong(y_obj));
} else {
PyErr_SetString(PyExc_TypeError, "Position y must be a number");
return -1;
}
} else {
// Try to get as Vector
PyObject* module = PyImport_ImportModule("mcrfpy");
if (!module) return -1;
PyObject* vector_type = PyObject_GetAttrString(module, "Vector");
Py_DECREF(module);
if (!vector_type) return -1;
int is_vector = PyObject_IsInstance(value, vector_type);
Py_DECREF(vector_type);
if (is_vector) {
PyVectorObject* vec = (PyVectorObject*)value;
x = vec->data.x;
y = vec->data.y;
} else {
PyErr_SetString(PyExc_TypeError, "Position must be a tuple (x, y) or Vector");
return -1;
}
}
drawable->position = sf::Vector2f(x, y);
drawable->onPositionChanged();
return 0;
}