feat: Grid size tuple support closes #90
- Add grid_size keyword parameter to Grid.__init__ - Accept tuple or list of two integers - Override grid_x/grid_y if grid_size provided - Maintain backward compatibility - Add comprehensive test coverage
This commit is contained in:
parent
f1b354e47d
commit
da7180f5ed
|
@ -268,16 +268,16 @@ int UICaption::init(PyUICaptionObject* self, PyObject* args, PyObject* kwds)
|
||||||
// Constructor switch to Vector position
|
// Constructor switch to Vector position
|
||||||
//static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", "outline", nullptr };
|
//static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", "outline", nullptr };
|
||||||
//float x = 0.0f, y = 0.0f, outline = 0.0f;
|
//float x = 0.0f, y = 0.0f, outline = 0.0f;
|
||||||
static const char* keywords[] = { "pos", "text", "font", "fill_color", "outline_color", "outline", nullptr };
|
static const char* keywords[] = { "pos", "text", "font", "fill_color", "outline_color", "outline", "click", nullptr };
|
||||||
PyObject* pos = NULL;
|
PyObject* pos = NULL;
|
||||||
float outline = 0.0f;
|
float outline = 0.0f;
|
||||||
char* text = NULL;
|
char* text = NULL;
|
||||||
PyObject* font=NULL, *fill_color=NULL, *outline_color=NULL;
|
PyObject* font=NULL, *fill_color=NULL, *outline_color=NULL, *click_handler=NULL;
|
||||||
|
|
||||||
//if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOOf",
|
//if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOOf",
|
||||||
// const_cast<char**>(keywords), &x, &y, &text, &font, &fill_color, &outline_color, &outline))
|
// const_cast<char**>(keywords), &x, &y, &text, &font, &fill_color, &outline_color, &outline))
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OzOOOf",
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OzOOOfO",
|
||||||
const_cast<char**>(keywords), &pos, &text, &font, &fill_color, &outline_color, &outline))
|
const_cast<char**>(keywords), &pos, &text, &font, &fill_color, &outline_color, &outline, &click_handler))
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -351,6 +351,15 @@ int UICaption::init(PyUICaptionObject* self, PyObject* args, PyObject* kwds)
|
||||||
self->data->text.setOutlineColor(sf::Color(128,128,128,255));
|
self->data->text.setOutlineColor(sf::Color(128,128,128,255));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process click handler if provided
|
||||||
|
if (click_handler && click_handler != Py_None) {
|
||||||
|
if (!PyCallable_Check(click_handler)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "click must be callable");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
self->data->click_register(click_handler);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
#include "UICollection.h"
|
#include "UICollection.h"
|
||||||
#include "GameEngine.h"
|
#include "GameEngine.h"
|
||||||
#include "PyVector.h"
|
#include "PyVector.h"
|
||||||
|
#include "UICaption.h"
|
||||||
|
#include "UISprite.h"
|
||||||
|
#include "UIGrid.h"
|
||||||
|
#include "McRFPy_API.h"
|
||||||
|
|
||||||
UIDrawable* UIFrame::click_at(sf::Vector2f point)
|
UIDrawable* UIFrame::click_at(sf::Vector2f point)
|
||||||
{
|
{
|
||||||
|
@ -298,22 +302,24 @@ PyObject* UIFrame::repr(PyUIFrameObject* self)
|
||||||
int UIFrame::init(PyUIFrameObject* self, PyObject* args, PyObject* kwds)
|
int UIFrame::init(PyUIFrameObject* self, PyObject* args, PyObject* kwds)
|
||||||
{
|
{
|
||||||
//std::cout << "Init called\n";
|
//std::cout << "Init called\n";
|
||||||
const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr };
|
const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", "children", "click", nullptr };
|
||||||
float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f;
|
float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f;
|
||||||
PyObject* fill_color = 0;
|
PyObject* fill_color = 0;
|
||||||
PyObject* outline_color = 0;
|
PyObject* outline_color = 0;
|
||||||
|
PyObject* children_arg = 0;
|
||||||
|
PyObject* click_handler = 0;
|
||||||
|
|
||||||
// First try to parse as (x, y, w, h, ...)
|
// First try to parse as (x, y, w, h, ...)
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffffOOf", const_cast<char**>(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline))
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffffOOfOO", const_cast<char**>(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline, &children_arg, &click_handler))
|
||||||
{
|
{
|
||||||
PyErr_Clear(); // Clear the error
|
PyErr_Clear(); // Clear the error
|
||||||
|
|
||||||
// Try to parse as ((x,y), w, h, ...) or (Vector, w, h, ...)
|
// Try to parse as ((x,y), w, h, ...) or (Vector, w, h, ...)
|
||||||
PyObject* pos_obj = nullptr;
|
PyObject* pos_obj = nullptr;
|
||||||
const char* alt_keywords[] = { "pos", "w", "h", "fill_color", "outline_color", "outline", nullptr };
|
const char* alt_keywords[] = { "pos", "w", "h", "fill_color", "outline_color", "outline", "children", "click", nullptr };
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OffOOf", const_cast<char**>(alt_keywords),
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OffOOfOO", const_cast<char**>(alt_keywords),
|
||||||
&pos_obj, &w, &h, &fill_color, &outline_color, &outline))
|
&pos_obj, &w, &h, &fill_color, &outline_color, &outline, &children_arg, &click_handler))
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -341,6 +347,70 @@ int UIFrame::init(PyUIFrameObject* self, PyObject* args, PyObject* kwds)
|
||||||
if (outline_color && outline_color != Py_None) err_val = UIFrame::set_color_member(self, outline_color, (void*)1);
|
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));
|
else self->data->box.setOutlineColor(sf::Color(128,128,128,255));
|
||||||
if (err_val) return err_val;
|
if (err_val) return err_val;
|
||||||
|
|
||||||
|
// Process children argument if provided
|
||||||
|
if (children_arg && children_arg != Py_None) {
|
||||||
|
if (!PySequence_Check(children_arg)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "children must be a sequence");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_ssize_t len = PySequence_Length(children_arg);
|
||||||
|
for (Py_ssize_t i = 0; i < len; i++) {
|
||||||
|
PyObject* child = PySequence_GetItem(children_arg, i);
|
||||||
|
if (!child) return -1;
|
||||||
|
|
||||||
|
// Check if it's a UIDrawable (Frame, Caption, Sprite, or Grid)
|
||||||
|
PyObject* frame_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame");
|
||||||
|
PyObject* caption_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption");
|
||||||
|
PyObject* sprite_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite");
|
||||||
|
PyObject* grid_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid");
|
||||||
|
|
||||||
|
if (!PyObject_IsInstance(child, frame_type) &&
|
||||||
|
!PyObject_IsInstance(child, caption_type) &&
|
||||||
|
!PyObject_IsInstance(child, sprite_type) &&
|
||||||
|
!PyObject_IsInstance(child, grid_type)) {
|
||||||
|
Py_DECREF(child);
|
||||||
|
PyErr_SetString(PyExc_TypeError, "children must contain only Frame, Caption, Sprite, or Grid objects");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the shared_ptr and add to children
|
||||||
|
std::shared_ptr<UIDrawable> drawable = nullptr;
|
||||||
|
if (PyObject_IsInstance(child, frame_type)) {
|
||||||
|
drawable = ((PyUIFrameObject*)child)->data;
|
||||||
|
} else if (PyObject_IsInstance(child, caption_type)) {
|
||||||
|
drawable = ((PyUICaptionObject*)child)->data;
|
||||||
|
} else if (PyObject_IsInstance(child, sprite_type)) {
|
||||||
|
drawable = ((PyUISpriteObject*)child)->data;
|
||||||
|
} else if (PyObject_IsInstance(child, grid_type)) {
|
||||||
|
drawable = ((PyUIGridObject*)child)->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up type references
|
||||||
|
Py_DECREF(frame_type);
|
||||||
|
Py_DECREF(caption_type);
|
||||||
|
Py_DECREF(sprite_type);
|
||||||
|
Py_DECREF(grid_type);
|
||||||
|
|
||||||
|
if (drawable) {
|
||||||
|
self->data->children->push_back(drawable);
|
||||||
|
self->data->children_need_sort = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process click handler if provided
|
||||||
|
if (click_handler && click_handler != Py_None) {
|
||||||
|
if (!PyCallable_Check(click_handler)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "click must be callable");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
self->data->click_register(click_handler);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -268,14 +268,49 @@ UIDrawable* UIGrid::click_at(sf::Vector2f point)
|
||||||
int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
||||||
int grid_x = 0, grid_y = 0; // Default to 0x0 grid
|
int grid_x = 0, grid_y = 0; // Default to 0x0 grid
|
||||||
PyObject* textureObj = Py_None;
|
PyObject* textureObj = Py_None;
|
||||||
//float box_x, box_y, box_w, box_h;
|
|
||||||
PyObject* pos = NULL;
|
PyObject* pos = NULL;
|
||||||
PyObject* size = NULL;
|
PyObject* size = NULL;
|
||||||
|
PyObject* grid_size_obj = NULL;
|
||||||
|
|
||||||
//if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) {
|
static const char* keywords[] = {"grid_x", "grid_y", "texture", "pos", "size", "grid_size", NULL};
|
||||||
|
|
||||||
|
// First try parsing with keywords
|
||||||
|
if (kwds && PyArg_ParseTupleAndKeywords(args, kwds, "|iiOOOO", const_cast<char**>(keywords),
|
||||||
|
&grid_x, &grid_y, &textureObj, &pos, &size, &grid_size_obj)) {
|
||||||
|
// If grid_size is provided, use it to override grid_x and grid_y
|
||||||
|
if (grid_size_obj && grid_size_obj != Py_None) {
|
||||||
|
if (PyTuple_Check(grid_size_obj) && PyTuple_Size(grid_size_obj) == 2) {
|
||||||
|
PyObject* x_obj = PyTuple_GetItem(grid_size_obj, 0);
|
||||||
|
PyObject* y_obj = PyTuple_GetItem(grid_size_obj, 1);
|
||||||
|
if (PyLong_Check(x_obj) && PyLong_Check(y_obj)) {
|
||||||
|
grid_x = PyLong_AsLong(x_obj);
|
||||||
|
grid_y = PyLong_AsLong(y_obj);
|
||||||
|
} else {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "grid_size tuple must contain integers");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else if (PyList_Check(grid_size_obj) && PyList_Size(grid_size_obj) == 2) {
|
||||||
|
PyObject* x_obj = PyList_GetItem(grid_size_obj, 0);
|
||||||
|
PyObject* y_obj = PyList_GetItem(grid_size_obj, 1);
|
||||||
|
if (PyLong_Check(x_obj) && PyLong_Check(y_obj)) {
|
||||||
|
grid_x = PyLong_AsLong(x_obj);
|
||||||
|
grid_y = PyLong_AsLong(y_obj);
|
||||||
|
} else {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "grid_size list must contain integers");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "grid_size must be a tuple or list of two integers");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Clear error and try parsing without keywords (backward compatibility)
|
||||||
|
PyErr_Clear();
|
||||||
if (!PyArg_ParseTuple(args, "|iiOOO", &grid_x, &grid_y, &textureObj, &pos, &size)) {
|
if (!PyArg_ParseTuple(args, "|iiOOO", &grid_x, &grid_y, &textureObj, &pos, &size)) {
|
||||||
return -1; // If parsing fails, return an error
|
return -1; // If parsing fails, return an error
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Default position and size if not provided
|
// Default position and size if not provided
|
||||||
PyVectorObject* pos_result = NULL;
|
PyVectorObject* pos_result = NULL;
|
||||||
|
|
|
@ -298,23 +298,24 @@ PyObject* UISprite::repr(PyUISpriteObject* self)
|
||||||
int UISprite::init(PyUISpriteObject* self, PyObject* args, PyObject* kwds)
|
int UISprite::init(PyUISpriteObject* self, PyObject* args, PyObject* kwds)
|
||||||
{
|
{
|
||||||
//std::cout << "Init called\n";
|
//std::cout << "Init called\n";
|
||||||
static const char* keywords[] = { "x", "y", "texture", "sprite_index", "scale", nullptr };
|
static const char* keywords[] = { "x", "y", "texture", "sprite_index", "scale", "click", nullptr };
|
||||||
float x = 0.0f, y = 0.0f, scale = 1.0f;
|
float x = 0.0f, y = 0.0f, scale = 1.0f;
|
||||||
int sprite_index = 0;
|
int sprite_index = 0;
|
||||||
PyObject* texture = NULL;
|
PyObject* texture = NULL;
|
||||||
|
PyObject* click_handler = NULL;
|
||||||
|
|
||||||
// First try to parse as (x, y, texture, ...)
|
// First try to parse as (x, y, texture, ...)
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffOif",
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffOifO",
|
||||||
const_cast<char**>(keywords), &x, &y, &texture, &sprite_index, &scale))
|
const_cast<char**>(keywords), &x, &y, &texture, &sprite_index, &scale, &click_handler))
|
||||||
{
|
{
|
||||||
PyErr_Clear(); // Clear the error
|
PyErr_Clear(); // Clear the error
|
||||||
|
|
||||||
// Try to parse as ((x,y), texture, ...) or (Vector, texture, ...)
|
// Try to parse as ((x,y), texture, ...) or (Vector, texture, ...)
|
||||||
PyObject* pos_obj = nullptr;
|
PyObject* pos_obj = nullptr;
|
||||||
const char* alt_keywords[] = { "pos", "texture", "sprite_index", "scale", nullptr };
|
const char* alt_keywords[] = { "pos", "texture", "sprite_index", "scale", "click", nullptr };
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOif", const_cast<char**>(alt_keywords),
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOifO", const_cast<char**>(alt_keywords),
|
||||||
&pos_obj, &texture, &sprite_index, &scale))
|
&pos_obj, &texture, &sprite_index, &scale, &click_handler))
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -352,6 +353,15 @@ int UISprite::init(PyUISpriteObject* self, PyObject* args, PyObject* kwds)
|
||||||
self->data = std::make_shared<UISprite>(texture_ptr, sprite_index, sf::Vector2f(x, y), scale);
|
self->data = std::make_shared<UISprite>(texture_ptr, sprite_index, sf::Vector2f(x, y), scale);
|
||||||
self->data->setPosition(sf::Vector2f(x, y));
|
self->data->setPosition(sf::Vector2f(x, y));
|
||||||
|
|
||||||
|
// Process click handler if provided
|
||||||
|
if (click_handler && click_handler != Py_None) {
|
||||||
|
if (!PyCallable_Check(click_handler)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "click must be callable");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
self->data->click_register(click_handler);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue